分叉后如何创建共享内存?

问题描述 投票:0回答:1

我的代码是

#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)却不能使用。

我是不是做错了什么?我怎样才能在子程序中创建共享内存,使父程序甚至兄弟程序(同一父程序的其他子程序)都能访问?

c linux shared-memory
1个回答
2
投票

匿名共享内存保持共享 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 -
© www.soinside.com 2019 - 2024. All rights reserved.