├── .cirrus.yml ├── .clang-format ├── .editorconfig ├── .gitignore ├── LICENSE ├── README.adoc ├── build.sh ├── shm_open_anon.c ├── shm_open_anon.h └── test_shared.c /.cirrus.yml: -------------------------------------------------------------------------------- 1 | alpine_task: 2 | container: 3 | image: alpine 4 | setup_script: 5 | - apk update 6 | - apk add build-base linux-headers 7 | compile_script: ./build.sh 8 | test_script: ./test_shared 9 | archlinux_task: 10 | container: 11 | image: archlinux/base 12 | setup_script: 13 | - pacman --noconfirm -Sy 14 | - pacman --noconfirm -S gcc 15 | compile_script: ./build.sh 16 | test_script: ./test_shared 17 | centos_task: 18 | container: 19 | image: centos 20 | setup_script: 21 | - yum update -y 22 | - yum install -y gcc glibc-devel kernel-headers 23 | compile_script: ./build.sh 24 | test_script: ./test_shared 25 | debian_task: 26 | container: 27 | image: debian 28 | setup_script: 29 | - apt update 30 | - apt install -y gcc libc-dev linux-headers-amd64 31 | compile_script: ./build.sh 32 | test_script: ./test_shared 33 | ubuntu_task: 34 | container: 35 | image: ubuntu 36 | setup_script: 37 | - apt update 38 | - apt install -y gcc libc-dev 39 | compile_script: ./build.sh 40 | test_script: ./test_shared 41 | freebsd_task: 42 | freebsd_instance: 43 | image_family: freebsd-12-1 44 | compile_script: ./build.sh 45 | test_script: ./test_shared 46 | macos_task: 47 | osx_instance: 48 | image: mojave-base 49 | compile_script: ./build.sh 50 | test_script: ./test_shared 51 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Mozilla 3 | TabWidth: 8 4 | IndentWidth: 8 5 | UseTab: ForIndentation 6 | AlwaysBreakAfterReturnType: TopLevel 7 | PointerAlignment: Right 8 | ColumnLimit: 78 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | tab_width = 8 9 | 10 | [*.c] 11 | indent_size = 8 12 | indent_style = tab 13 | 14 | [*.sh] 15 | indent_size = 8 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | /test_shared 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Lassi Kortela 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = shm_open_anon 2 | 3 | image::https://api.cirrus-ci.com/github/lassik/shm_open_anon.svg[Build Status, link=https://cirrus-ci.com/github/lassik/shm_open_anon] 4 | 5 | == Overview 6 | 7 | This library provides a single C function: 8 | 9 | int shm_open_anon(void); 10 | 11 | It returns a file descriptor pointing to a shared memory region. On 12 | failure, `-1` is returned and `errno` is set. 13 | 14 | == Details 15 | 16 | The region is not bound to any pathname -- it is anonymous like the 17 | ones you get from `mmap()`. But whereas `mmap()` gives you a pointer, 18 | this one gives you a file descriptor, which means you can pass it to 19 | other processes even if they are not direct forks of the process that 20 | made it. 21 | 22 | On arrival, the descriptor has the close-on-exec flag set so it 23 | doesn't survive an `exec()` boundary. But you can call `fcntl()` to 24 | unset the `FD_CLOEXEC` flag to make it survive. Then you can freely 25 | pass it to subprocesses that start other executables. Make sure to 26 | place it at a known file descriptor number (usually number `3` and up) 27 | using `dup2()` so the other executable can find it. 28 | 29 | You can also use `sendmsg()` with `SCM_RIGHTS` to send a copy of the 30 | file descriptor to another process over a Unix domain socket. That 31 | part of the BSD socket API is even more perplexing than the rest of 32 | it, so there's a convenience wrapper _libancillary_ that offers "fd 33 | passing for humans". 34 | 35 | The region's initial size is zero bytes. You need to use `ftruncate()` 36 | to grow it, and then you'll probably want to use `mmap()` to get a 37 | pointer to actually access the memory. `mmap()` needs to know the size 38 | of the region. If you pass the file descriptor to an unrelated 39 | process, that process can use `fstat()` and then look at `st_size` in 40 | the result. 41 | 42 | Because the file descriptor is not bound to any file system or shared 43 | memory pathname, you don't need to worry about a memory leak in case 44 | your processes terminate abruptly. There's no need to do special 45 | cleanup -- the operating system removes the shared memory object when 46 | all file descriptors accessing it are closed. 47 | 48 | == Implementation details 49 | 50 | === Linux 51 | 52 | ==== Current method using memfd 53 | 54 | This technique is available in Linux kernel version 3.17 and later. 55 | 56 | - Use `memfd_create()` -- problem solved. 57 | 58 | - `memfd_create()` is a system call that exists since kernel version 59 | 3.17. 60 | 61 | - Even if the syscall is in your kernel, your libc does not 62 | necessarily have a C function (syscall wrapper) for it. GNU libc 63 | took years to add a wrapper. 64 | 65 | - You can get around that by calling the generic syscall wrapper 66 | `syscall(__NR_memfd_create, ...)` instead. Remember to typecast the 67 | arguments. 68 | 69 | - `__NR_memfd_create` is the Linux syscall number (a small integer). 70 | Note that syscall numbers may differ by computer architecture. Use 71 | `#include ` to get the right numbers for your 72 | architecture. 73 | 74 | - Since `\__NR_memfd_create` is a preprocessor definition, you can use 75 | `#ifdef __NR_memfd_create` to check whether your Linux headers 76 | define it. 77 | 78 | - If the syscall is not implemented in the kernel you are running, you 79 | get an errno value of `ENOSYS` or `ENOTSUP` (not sure which one). 80 | 81 | - http://man7.org/linux/man-pages/man2/memfd_create.2.html 82 | 83 | ==== Old method using /dev/shm 84 | 85 | This technique is not included in the current library since all 86 | currently maintained Linux kernel versions implement the newer and 87 | more robust `memfd` API, but it is still useful where compatibility 88 | with older systems is needed. 89 | 90 | - In kernel versions before 3.17, a memory-backed file system was 91 | mounted into `/dev/shm` or `/run/shm` depending on the distro. (In 92 | fact, distros still mount such a file system, but it's a bit less 93 | useful now.) 94 | 95 | - You can use perfectly ordinary `open()` and `unlink()` to operate on 96 | any files there. 97 | 98 | - The safest and easiest way create a tempfile there is `mkostemp()`. 99 | It's in fact easier to use this than to use `shm_open()`. 100 | 101 | - To force a file to be opened as a memory-backed tempfile instead of 102 | a disk file, regardless of which file system its pathname points to, 103 | you can give `O_TMPFILE` to `open()` since kernel version 3.11. You 104 | really should use `O_RDWR` along with it. Note also that `O_TMPFILE` 105 | may not be defined by library headers, but that constant is probably 106 | architecture independent so you may be able to get away with 107 | defining it yourself. 108 | 109 | - http://man7.org/linux/man-pages/man7/shm_overview.7.html 110 | 111 | - https://lwn.net/Articles/619146/ (how `O_TMPFILE` came about) 112 | 113 | === FreeBSD 114 | 115 | - Use `shm_open(SHM_ANON, O_RDWR, 0)` -- problem solved. 116 | 117 | - The constant `SHM_ANON` is defined to be `1` which is an unaligned 118 | pointer one byte away from the null pointer `0`. 119 | 120 | - You must use the `O_RDWR` flag. If you do not, the default is 121 | `O_RDONLY` (value zero) and that's not allowed for shm objects. 122 | 123 | - Permission bits can be zero, at least when subprocesses have the 124 | same user ID as the parent process that created the fd. 125 | 126 | - https://www.freebsd.org/cgi/man.cgi?query=shm_open 127 | 128 | - https://github.com/freebsd/freebsd/search?q=SHM_ANON 129 | 130 | === OpenBSD 131 | 132 | - `shm_mkstemp()` is the thing to use. You need to `shm_unlink()` the 133 | path afterwards. 134 | 135 | - Pathnames given to `shm_open()` get translated into 136 | `/tmp/.shm` where `` is the SHA-256 hash of the pathname 137 | you gave. It doesn't matter what slashes, if any, your pathname has. 138 | 139 | - `shm_mkstemp()` calls `shm_open()` with `O_RDWR | O_EXCL | O_CREAT` 140 | in a loop until it succeeds. Your pathname template gets the X's 141 | filled in as with `mktemp()` and then `shm_open()` applies its 142 | translation rules to that. So it doesn't much matter what pathname 143 | you give. 144 | 145 | - `shm_unlink()` translates path to shm path and does `unlink()`. 146 | 147 | - http://man.openbsd.org/shm_mkstemp.3 148 | 149 | - https://github.com/openbsd/src/blob/master/lib/libc/gen/shm_open.c 150 | 151 | === NetBSD 152 | 153 | - `shm_open()` is the best we can do. You need to `shm_unlink()` the 154 | path afterwards. 155 | 156 | - The pathname given to `shm_open()` must start with a slash. It must 157 | *not have any other* slashes. 158 | 159 | - If the pathname does not start with a slash, or has other slashes, 160 | you get `EINVAL`. 161 | 162 | - Each pathname `/foo` is translated into `/var/shm/.shmobj_foo`. 163 | 164 | - `/var/shm` is mounted as a tmpfs filesystem. The shm routines check 165 | this and if is't not, you get `ENOTSUP`. 166 | 167 | - `shm_open()` translates your path to an shm path and then does 168 | `open()` with `O_CLOEXEC | O_NOFOLLOW`. 169 | 170 | - `shm_unlink()` translates your path to an shm path and then does 171 | `unlink()`. 172 | 173 | - http://netbsd.gw.com/cgi-bin/man-cgi?shm_open 174 | 175 | - https://github.com/NetBSD/src/blob/trunk/lib/librt/shm.c 176 | 177 | === DragonFly BSD 178 | 179 | - `shm_open()` is the best we can do. You need to `shm_unlink()` the 180 | path afterwards. 181 | 182 | - `shm_open()` does `open()` but also uses `fcntl()` to set the 183 | undocumented `FPOSIXSHM` flag. It also sets `FD_CLOEXEC`. 184 | 185 | - `shm_unlink()` does `unlink()`. 186 | 187 | - Before 5.6.0 there was no pathname translation at all. Starting with 188 | 5.6.0 a tmpfs file system is mounted at `/var/run/shm` during boot, 189 | and shm pathnames are taken relative to that directory (with any 190 | number of leading slashes removed from the user-supplied pathname). 191 | 192 | - To generate the pathname, I couldn't come up with anything better 193 | than generating a random filename of the form `/shm-XXXXXXX`. 194 | 195 | - https://leaf.dragonflybsd.org/cgi/web-man?command=shm_open§ion=3 196 | 197 | - https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/lib/libc/gen/posixshm.c 198 | 199 | === MacOS X 200 | 201 | - I didn't find anything better than `shm_open()` and `shm_unlink()` 202 | with POSIX semantics. 203 | 204 | === Solaris 205 | 206 | - I didn't find anything better than `shm_open()` and `shm_unlink()` 207 | with POSIX semantics. 208 | 209 | - Pathnames given to `shm_open()` get translated into either 210 | `/tmp/.SHMD` or `/tmp/./.SHMD/` where `` is 211 | the MD5 hash of the pathname you gave. There are also equivalent 212 | `.SHML` lock files. 213 | 214 | - https://docs.oracle.com/cd/E26505_01/html/816-5171/shm-open-3rt.html 215 | 216 | - https://docs.oracle.com/cd/E26505_01/html/816-5171/shm-unlink-3rt.html 217 | 218 | - https://github.com/kofemann/opensolaris/blob/master/usr/src/lib/libc/port/rt/shm.c 219 | 220 | - https://github.com/kofemann/opensolaris/blob/master/usr/src/lib/libc/port/rt/pos4obj.c 221 | 222 | === Haiku (BeOS) 223 | 224 | - I didn't find anything better than `shm_open()` and `shm_unlink()`. 225 | 226 | - Translates your pathname so it goes under the `/var/shared_memory` 227 | directory. Removes any number of leading slashes, then escapes `/` 228 | by `%s` and `%` by `%%` (these are literal percent signs, not format 229 | string magic). 230 | 231 | - Othersise `shm_open()` and `shm_unlink()` are just `open()` and 232 | `unlink()`. `shm_open()` opens with `FD_CLOEXEC`. 233 | 234 | - Not sure whether or not the original BeOS had these same semantics. 235 | 236 | - https://github.com/haiku/haiku/blob/master/src/system/libroot/posix/sys/mman.cpp 237 | 238 | === Cygwin 239 | 240 | - Probably have to use `shm_open()` and `shm_unlink()`. 241 | 242 | - Not sure if clearing the close-on-exec flag and using `dup2()` will 243 | have the desired effect. 244 | 245 | - Translates your pathname by removing at most one slash from the 246 | beginning. Then puts that name under `/dev/shm/` with no escaping of 247 | slashes. So it's best to use a name that has only one slash with the 248 | start; if you use more slashes, those subdirectories may have to 249 | exist under `/dev/shm`. 250 | 251 | - As far as I can tell, `/dev/shm` is an ordinary directory on a 252 | disk-backed file system, not a special memory-back file system. So 253 | expect shared memory to be slow, especially on traditional hard 254 | disks. 255 | 256 | - Cygwin also supports System V IPC (`shmget()` et.al.) and it seems 257 | to be specially implemented by `cygserver` on a better foundation. 258 | 259 | - https://github.com/Alexpux/Cygwin/blob/master/newlib/libc/sys/linux/shm_open.c 260 | 261 | - http://pipeline.lbl.gov/code/3rd_party/licenses.win/Cygwin/cygserver.README 262 | 263 | == Credits 264 | 265 | Chris Wellons wrote a thoughtful blog post 266 | (https://nullprogram.com/blog/2016/04/10/[Mapping Multiple Memory 267 | Views in User Space], 2016-04-10) detailing how to use `shm_open()` 268 | without a filename. It also covers Windows API equivalents to 269 | `shm_open()` and `mmap()`, which are `CreateFileMapping()` and 270 | `MapViewOfFile()`. 271 | 272 | Ludovic P improved Linux portability and helped design the random 273 | filename generator for `shm_open()`. 274 | 275 | Maxim Egorushkin suggested using plain `mkostemp("/dev/shm/..." ,...)` 276 | instead of `shm_open()` on Linux. 277 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eu 3 | cd "$(dirname "$0")" 4 | default_cc=gcc 5 | default_cflags="-Og -g -Wall -Werror -Wextra -pedantic -std=gnu99" 6 | default_lflags="" 7 | case "$(uname)" in 8 | Darwin) default_cc=clang ;; 9 | DragonFly) ;; 10 | FreeBSD) default_cc=clang ;; 11 | Haiku) default_cflags="-g -Wall -Werror" ;; 12 | Linux) default_lflags="-lrt" ;; 13 | NetBSD) default_lflags="-lrt" ;; 14 | OpenBSD) default_cc=clang ;; 15 | SunOS) ;; 16 | *) 17 | echo "Operating system not supported: $(uname)" >&2 18 | exit 1 19 | ;; 20 | esac 21 | CC="${CC:-$default_cc}" 22 | CFLAGS="${CFLAGS:-$default_cflags}" 23 | LFLAGS="${LFLAGS:-$default_lflags}" 24 | echo "Entering directory '$PWD'" 25 | set -x 26 | $CC $CFLAGS -c shm_open_anon.c 27 | $CC $CFLAGS -c test_shared.c 28 | $CC $LFLAGS -o test_shared test_shared.o shm_open_anon.o 29 | -------------------------------------------------------------------------------- /shm_open_anon.c: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lassi Kortela 2 | // SPDX-License-Identifier: ISC 3 | 4 | #ifdef __linux__ 5 | #define _GNU_SOURCE 6 | #include 7 | #include 8 | #endif 9 | 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #undef IMPL_MEMFD 21 | #undef IMPL_POSIX 22 | #undef IMPL_SHM_ANON 23 | #undef IMPL_SHM_MKSTEMP 24 | #undef IMPL_UNLINK_OR_CLOSE 25 | 26 | #ifdef __linux__ 27 | #ifdef __NR_memfd_create 28 | #define IMPL_MEMFD 29 | #endif 30 | #endif 31 | 32 | #ifdef __FreeBSD__ 33 | #define IMPL_SHM_ANON 34 | #endif 35 | 36 | #ifdef __HAIKU__ 37 | #define IMPL_POSIX 38 | #endif 39 | 40 | #ifdef __NetBSD__ 41 | #define IMPL_POSIX 42 | #endif 43 | 44 | #ifdef __APPLE__ 45 | #ifdef __MACH__ 46 | #define IMPL_POSIX 47 | #endif 48 | #endif 49 | 50 | #ifdef __sun 51 | #define IMPL_POSIX 52 | #endif 53 | 54 | #ifdef __DragonFly__ 55 | #define IMPL_POSIX 56 | #endif 57 | 58 | #ifdef __OpenBSD__ 59 | #define IMPL_SHM_MKSTEMP 60 | #endif 61 | 62 | #ifdef IMPL_POSIX 63 | #define IMPL_UNLINK_OR_CLOSE 64 | #endif 65 | 66 | #ifdef IMPL_SHM_MKSTEMP 67 | #define IMPL_UNLINK_OR_CLOSE 68 | #endif 69 | 70 | #ifdef IMPL_UNLINK_OR_CLOSE 71 | static int 72 | shm_unlink_or_close(const char *name, int fd) 73 | { 74 | int save; 75 | 76 | if (shm_unlink(name) == -1) { 77 | save = errno; 78 | close(fd); 79 | errno = save; 80 | return -1; 81 | } 82 | return fd; 83 | } 84 | #endif 85 | 86 | #ifdef IMPL_POSIX 87 | int 88 | shm_open_anon(void) 89 | { 90 | char name[16] = "/shm-"; 91 | struct timespec tv; 92 | unsigned long r; 93 | char *const limit = name + sizeof(name) - 1; 94 | char *start; 95 | char *fill; 96 | int fd, tries; 97 | 98 | *limit = 0; 99 | start = name + strlen(name); 100 | for (tries = 0; tries < 4; tries++) { 101 | clock_gettime(CLOCK_REALTIME, &tv); 102 | r = (unsigned long)tv.tv_sec + (unsigned long)tv.tv_nsec; 103 | for (fill = start; fill < limit; r /= 8) 104 | *fill++ = '0' + (r % 8); 105 | fd = shm_open( 106 | name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); 107 | if (fd != -1) 108 | return shm_unlink_or_close(name, fd); 109 | if (errno != EEXIST) 110 | break; 111 | } 112 | return -1; 113 | } 114 | #endif 115 | 116 | #ifdef IMPL_SHM_MKSTEMP 117 | int 118 | shm_open_anon(void) 119 | { 120 | char name[16] = "/shm-XXXXXXXXXX"; 121 | int fd; 122 | 123 | if ((fd = shm_mkstemp(name)) == -1) 124 | return -1; 125 | return shm_unlink_or_close(name, fd); 126 | } 127 | #endif 128 | 129 | #ifdef IMPL_SHM_ANON 130 | int 131 | shm_open_anon(void) 132 | { 133 | return shm_open(SHM_ANON, O_RDWR, 0); 134 | } 135 | #endif 136 | 137 | #ifdef IMPL_MEMFD 138 | int 139 | shm_open_anon(void) 140 | { 141 | return syscall( 142 | __NR_memfd_create, "shm_anon", (unsigned int)(MFD_CLOEXEC)); 143 | } 144 | #endif 145 | -------------------------------------------------------------------------------- /shm_open_anon.h: -------------------------------------------------------------------------------- 1 | int shm_open_anon(void); 2 | -------------------------------------------------------------------------------- /test_shared.c: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lassi Kortela 2 | // SPDX-License-Identifier: ISC 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "shm_open_anon.h" 18 | 19 | #define PROGNAME "test_shared" 20 | 21 | static void 22 | diesys(const char *msg) 23 | { 24 | fprintf(stderr, "%s: %s\n", msg, strerror(errno)); 25 | exit(1); 26 | } 27 | 28 | static int 29 | fd_without_close_on_exec(int fd) 30 | { 31 | int flags; 32 | 33 | if ((flags = fcntl(fd, F_GETFD)) == -1) 34 | diesys("get close-on-exec"); 35 | flags &= ~FD_CLOEXEC; 36 | if (fcntl(fd, F_SETFD, flags) == -1) 37 | diesys("set close-on-exec"); 38 | return fd; 39 | } 40 | 41 | static int 42 | shm_open_anon_shared(void) 43 | { 44 | int fd; 45 | 46 | if ((fd = shm_open_anon()) == -1) 47 | diesys("shm_open_anon()"); 48 | return fd_without_close_on_exec(fd); 49 | } 50 | 51 | static void * 52 | map_shared_memory_from_fd(int fd, size_t *out_size) 53 | { 54 | struct stat st; 55 | void *buf; 56 | size_t size; 57 | 58 | if (fstat(fd, &st) == -1) { 59 | diesys("fstat"); 60 | } 61 | *out_size = size = (size_t)st.st_size; 62 | buf = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 63 | if (buf == MAP_FAILED) { 64 | diesys("mmap"); 65 | } 66 | return buf; 67 | } 68 | 69 | static void 70 | parent(void) 71 | { 72 | char *child_args[3] = { "./" PROGNAME, "child", 0 }; 73 | char *buf; 74 | size_t bufsize; 75 | pid_t child; 76 | int fd, status; 77 | 78 | bufsize = 10 * 1024 * 1024; 79 | if ((fd = shm_open_anon_shared()) == -1) 80 | diesys("shm_open_anon_shared"); 81 | if (ftruncate(fd, (off_t)bufsize) == -1) 82 | diesys("ftruncate"); 83 | buf = map_shared_memory_from_fd(fd, &bufsize); 84 | memset(buf, 0, bufsize); 85 | snprintf(buf, bufsize, "hello from parent"); 86 | if ((child = fork()) == -1) 87 | diesys("fork"); 88 | if (!child) { 89 | if (dup2(fd, 3) == -1) 90 | diesys("dup2"); 91 | execv(child_args[0], child_args); 92 | diesys("execv"); 93 | } 94 | if (waitpid(child, &status, 0) == -1) 95 | diesys("cannot wait for child"); 96 | printf("Parent: %s\n", buf); 97 | } 98 | 99 | static void 100 | child(void) 101 | { 102 | char *buf; 103 | size_t bufsize; 104 | 105 | buf = map_shared_memory_from_fd(3, &bufsize); 106 | printf("Child: %s\n", buf); 107 | snprintf(buf, bufsize, "hello from child"); 108 | } 109 | 110 | int 111 | main(int argc, char **argv) 112 | { 113 | (void)argv; 114 | if (argc == 1) { 115 | parent(); 116 | } else if ((argc == 2) && !strcmp(argv[1], "child")) { 117 | child(); 118 | } else { 119 | fprintf(stderr, "usage\n"); 120 | exit(1); 121 | } 122 | return 0; 123 | } 124 | --------------------------------------------------------------------------------