fork后如何创建共享内存?
Posted
技术标签:
【中文标题】fork后如何创建共享内存?【英文标题】:How to create shared memory after fork? 【发布时间】:2020-06-11 09:03:05 【问题描述】:我的代码是
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
int main()
char parent[] = "parent";
char child[] = "child";
char *shmem = (char*)mmap(NULL, 1000, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
char *shmem_child = "NOT CHANGE";
memcpy(shmem, parent, sizeof(parent));
int pid = fork();
if (pid == 0)
char child_new[] = "new child";
printf("Child read: %s\n", shmem);
memcpy(shmem, child, sizeof(child));
printf("Child wrote: %s\n", shmem);
shmem_child = (char*)mmap(NULL, 1000, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
memcpy(shmem_child, child_new, sizeof(child_new));
printf("Child create: %s\n", shmem_child);
else
printf("Parent read: %s\n", shmem);
sleep(1);
printf("After 1s, parent read: %s\n", shmem);
printf("After 1s, parent read shmem_child: %s\n", shmem_child);
输出是
Parent read: parent
Child read: parent
Child wrote: child
Child create: new child
After 1s, parent read: child
After 1s, parent read shmem_child: NOT CHANGE
可以看到,fork之前创建的共享内存(shmem)可以工作,但是child内部创建的共享内存(shmem_child)却不行。
我做错了吗?如何在孩子内部创建共享内存,以便父母甚至兄弟(同一父母的其他孩子)可以访问?
【问题讨论】:
【参考方案1】:匿名共享内存在fork()
之间保持共享。
所以,父母和孩子都应该访问相同的内存区域,shmem
。
您不能在子进程中创建匿名共享内存,并让它神奇地出现在父进程中。必须在父级中创建匿名共享内存;那么所有孩子都可以访问它。
您可以创建非匿名共享内存,例如通过shm_open()。创建者ftruncate()
s 将其设置为适当的长度,并且所有进程都对描述符进行内存映射。您确实需要记住在不再需要时通过 shm_unlink() 删除共享内存。
这是一个简单的(经过测试和验证的)父子之间匿名共享内存的示例:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
/* SPDX-License-Identifier: CC0-1.0 */
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef struct
char message[256];
shared_mem;
static size_t page_aligned(const size_t size)
const size_t page = sysconf(_SC_PAGESIZE);
if (size <= page)
return page;
else
return page * (size_t)(size / page + !!(size % page));
/* !!(size % page) is 0 if size is a multiple of page, 1 otherwise. */
int child_process(shared_mem *shared)
printf("Child: shared memory contains \"%s\".\n", shared->message);
fflush(stdout);
snprintf(shared->message, sizeof shared->message, "Child");
printf("Child: changed shared memory to \"%s\".\n", shared->message);
fflush(stdout);
return EXIT_SUCCESS;
int main(void)
const size_t size = page_aligned(sizeof (shared_mem));
shared_mem *shared;
pid_t child, p;
shared = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, (off_t)0);
if (shared == MAP_FAILED)
fprintf(stderr, "Cannot map %zu bytes of shared memory: %s.\n", size, strerror(errno));
return EXIT_FAILURE;
snprintf(shared->message, sizeof shared->message, "Parent");
printf("Parent: set shared memory to \"%s\".\n", shared->message);
fflush(stdout);
/* Create the child process. */
child = fork();
if (!child)
/* This is the child process. */
return child_process(shared);
else
if (child == -1)
fprintf(stderr, "Cannot create a child process: %s.\n", strerror(errno));
return EXIT_FAILURE;
/* This is the parent process. */
/* Wait for the child to exit. */
do
p = waitpid(child, NULL, 0);
while (p == -1 && errno == EINTR);
if (p == -1)
fprintf(stderr, "Cannot reap child process: %s.\n", strerror(errno));
return EXIT_FAILURE;
/* Describe the shared memory, */
printf("Parent: shared memory contains \"%s\".\n", shared->message);
fflush(stdout);
/* and tear it down. Done. */
munmap(shared, size);
return EXIT_SUCCESS;
另存为,例如example.c
,然后通过例如编译运行它
gcc -Wall -Wextra -O2 example1.c -o ex1
./ex1
会输出
Parent: set shared memory to "Parent".
Child: shared memory contains "Parent".
Child: changed shared memory to "Child".
Parent: shared memory contains "Child".
表明这确实有效。
要在 fork() 之后或在不相关的进程之间创建共享内存,所有进程必须就名称达成一致。对于 POSIX 共享内存对象(您获得使用 shm_open() 的描述符,名称必须以斜杠开头。
请注意,我使用的是模式 0600,它对应于(十进制 384)-rw-------,即只能由以同一用户身份运行的进程访问。
考虑以下示例:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
/* SPDX-License-Identifier: CC0-1.0 */
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
typedef struct
pid_t changer;
time_t when;
char message[256];
shared_mem;
static size_t page_aligned(const size_t size)
const size_t page = sysconf(_SC_PAGESIZE);
if (size <= page)
return page;
else
return page * (size_t)(size / page + !!(size % page));
/* !!(size % page) is 0 if size is a multiple of page, 1 otherwise. */
enum
ACTION_NONE = 0,
ACTION_CREATE = (1<<0),
ACTION_REMOVE = (1<<1),
ACTION_MODIFY = (1<<2),
;
int main(int argc, char *argv[])
const size_t size = page_aligned(sizeof (shared_mem));
shared_mem *shared;
const char *name;
time_t now;
const char *message = NULL;
int action = ACTION_NONE;
int arg, shm_fd;
if (argc < 2 || argc > 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
const char *argv0 = (argc > 0 && argv && argv[0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s /NAME [ +CREATE ] [ MESSAGE ] [ -REMOVE ]\n", argv0);
fprintf(stderr, "\n");
return EXIT_FAILURE;
/* Grab and check name */
name = argv[1];
if (name[0] != '/' || name[1] == '\0')
fprintf(stderr, "%s: Shared memory name must begin with a slash.\n", argv[1]);
return EXIT_FAILURE;
/* Check other command-line parameters. */
for (arg = 2; arg < argc; arg++)
if (argv[arg][0] == '+')
action |= ACTION_CREATE;
if (argv[arg][1] != '\0')
message = argv[arg] + 1;
action |= ACTION_MODIFY;
else
if (argv[arg][0] == '-')
action |= ACTION_REMOVE;
if (argv[arg][1] != '\0')
message = argv[arg] + 1;
action |= ACTION_MODIFY;
else
if (argv[arg][0] != '\0')
if (message)
fprintf(stderr, "%s: Can only set one message (already setting '%s').\n", argv[arg], message);
return EXIT_FAILURE;
message = argv[arg];
action |= ACTION_MODIFY;
if (action & ACTION_CREATE)
/* Create the shared memory object. */
shm_fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (shm_fd == -1)
fprintf(stderr, "%s: Cannot create shared memory object: %s.\n", name, strerror(errno));
return EXIT_FAILURE;
/* Resize it to desired size. */
if (ftruncate(shm_fd, (off_t)size) == -1)
fprintf(stderr, "%s: Cannot resize shared memory object to %zu bytes: %s.\n", name, size, strerror(errno));
close(shm_fd);
shm_unlink(name);
return EXIT_FAILURE;
else
/* Open an existing shared memory object. */
shm_fd = shm_open(name, O_RDWR, 0600);
if (shm_fd == -1)
fprintf(stderr, "%s: Cannot open shared memory object: %s.\n", name, strerror(errno));
return EXIT_FAILURE;
/* Map the shared memory object. */
shared = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, shm_fd, (off_t)0);
if (shared == MAP_FAILED)
fprintf(stderr, "%s: Cannot map %zu bytes of shared memory object: %s.\n", name, size, strerror(errno));
close(shm_fd);
if (action & (ACTION_CREATE | ACTION_REMOVE))
shm_unlink(name);
return EXIT_FAILURE;
/* The shared memory object descriptor is no longer needed. */
if (close(shm_fd) == -1)
fprintf(stderr, "Warning: Error closing shared memory object: %s.\n", strerror(errno));
/* Current time in UTC */
now = time(NULL);
/* If we created it, we need to initialize it too. */
if (action & ACTION_CREATE)
shared->changer = getpid();
shared->when = now;
snprintf(shared->message, sizeof shared->message, "Initialized");
/* Show contents. */
printf("Shared memory was last changed %ld seconds ago by process %ld to '%s'.\n",
(long)(now - shared->when), (long)(shared->changer), shared->message);
fflush(stdout);
/* Modify contents. */
if (action & ACTION_MODIFY)
printf("Changing shared memory contents into '%s'.\n", message);
fflush(stdout);
shared->changer = getpid();
shared->when = now;
snprintf(shared->message, sizeof shared->message, "%s", message);
/* Unmap shared memory object. */
munmap(shared, size);
/* Remove shared memory? */
if (action & ACTION_REMOVE)
if (shm_unlink(name) == -1)
fprintf(stderr, "Warning: %s: Cannot remove shared memory object: %s.\n", name, strerror(errno));
return EXIT_FAILURE;
else
printf("%s: Shared memory object removed successfully.\n", name);
fflush(stdout);
/* All done. */
return EXIT_SUCCESS;
另存为,例如example2.c
,并使用例如编译它
gcc -Wall -Wextra -O2 example2.c -lrt -o ex2
打开多个窗口。合二为一,运行
./ex2 /myshared +
创建共享内存;在其他情况下,运行
./ex2 /myshared newmessage
完成后,记得删除共享内存对象使用
./ex2 /myshared -
【讨论】:
以上是关于fork后如何创建共享内存?的主要内容,如果未能解决你的问题,请参考以下文章