我的代码是
#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)可以使用,但在子内存内创建的共享内存(shmem_child)却不能使用。
我是不是做错了什么?我怎样才能在子程序中创建共享内存,使父程序甚至兄弟程序(同一父程序的其他子程序)都能访问?
匿名共享内存保持共享 fork()
.
所以,父母和子女都应该访问同一个内存区域。shmem
.
你不能在子进程中创建匿名共享内存,然后让它神奇地出现在父进程中。 匿名共享内存必须在父进程中创建;然后它将被所有子进程访问。
你可以创建非匿名共享内存,比如通过 shm_open(). 创作人 ftruncate()
的描述符,所有进程都会对其进行内存映射。 当不再需要共享内存时,你需要记住通过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
,然后编译并通过e.g.运行它。
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(),名字必须以斜线开头。
请看下面的例子。
#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;
}
保存为e.g. example2.c
,并使用e.g.编译。
gcc -Wall -Wextra -O2 example2.c -lrt -o ex2
打开多个窗口。在一个窗口中,运行
./ex2 /myshared +
来创建共享内存;而在其他情况下,运行
./ex2 /myshared newmessage
当你完成后,记得使用-----------------------------删除共享内存对象。
./ex2 /myshared -