与struct和malloc共享内存fork

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了与struct和malloc共享内存fork相关的知识,希望对你有一定的参考价值。

我有一个fork生成的父进程和子进程之间的共享内存问题。我知道如何使用原始类型的共享内存,如here所述。

虽然我不知道如何共享包含可以通过malloc分配的指针的struct。

例如,假设我从上一个链接获取了以下代码并进行了一些修改。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h> 
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/wait.h>

typedef struct Data {
    char * name;
} Data;

void error_and_die(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]) {
    int r;

    const char *memname = "sample";
    const size_t region_size = sysconf(_SC_PAGE_SIZE);

    int fd = shm_open(memname, O_CREAT | O_TRUNC | O_RDWR, 0666);
    if (fd == -1)
        error_and_die("shm_open");

    r = ftruncate(fd, region_size);
    if (r != 0)
        error_and_die("ftruncate");

    Data * data = (Data *) malloc(sizeof(data));
    data->name=(char *) malloc(100*sizeof(char));   

    void *ptr = mmap(0, region_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED)
        error_and_die("mmap");
    close(fd);

    ptr=(Data *) data; 

    pid_t pid = fork();

    if (pid == 0) {
        //u_long *d = (u_long *) ptr;
        //*d = 0xdbeebee;
        data->name="bob";
        printf("CHILD child wrote %s
", (*(Data *)ptr).name);
        exit(0);
    }
    else {
        int status;
        waitpid(pid, &status, 0);
        //printf("child wrote %#lx
", *(u_long *) ptr);
        printf("PARENT child wrote %s
", (*(Data *)ptr).name);
    }

    r = munmap(ptr, region_size);
    if (r != 0)
        error_and_die("munmap");

    r = shm_unlink(memname);
    if (r != 0)
        error_and_die("shm_unlink");

    return 0;
}

我想在父进程和子进程之间更改结构内部的数据以允许IPC。我能怎么做?

谢谢

答案

如果您问“如何使用malloc()”,则不可能,除非您编写自己的malloc()实现,该实现从共享内存区域中的缓冲区分配。这是一种可能的方法,但超出了这个答案。否则,malloc()分配的内存不会在父项和子项之间共享;每个人都得到它自己的副本。

如果您正在询问“如何更改示例以使其正常工作” - 只需使用mmap()调用返回的内存即可。

为简化起见,让我们将name声明为Data结构中的数组:

typedef struct Data {
    char name[100];
} Data;

然后主代码变成:

void *ptr = mmap(0, region_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
    error_and_die("mmap");
close(fd);

Data * data = ptr;

pid_t pid = fork();

if (pid == 0) {
    strcpy(data->name, "bob");
    printf("CHILD child wrote %s
", (*(Data *)ptr).name);
    exit(0);
}
else {
    int status;
    waitpid(pid, &status, 0);
    printf("PARENT child wrote %s
", (*(Data *)ptr).name);
}

输出:

孩子写了鲍勃

父母的孩子写了鲍勃

另一答案

我通过跟随这个guide并使用shmget,shmat,smhdt和shmctl而不是shm_open,mmap,munmap和shm_unlink来解决。

通过这种方式,我可以像使用malloc一样管理动态共享内存,其优点是内存在进程之间是可见的。

我在这里报告代码作为未来提示。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>

typedef struct Person {
    char * name;
    int * numbers;
} Person;

typedef struct Data {
    Person * persons;
} Data;

int main(int argc, char *argv[]) {

    Data * data;
    Person * person;

    key_t mykey1=5500;
    key_t mykey2=5501;
    key_t mykey3=5502;
    key_t mykey4=5503;
    key_t mykey5=5504;
    int mem_id;

    mem_id=shmget(mykey1,sizeof(Person),IPC_CREAT|0666);
    if (mem_id<0) {
        perror("error shmget");
    }
    person=(Person*)shmat(mem_id,(void*)0,0);
    if(person == (Person*)(-1)) {
        perror("error shmat");
    }

    mem_id=shmget(mykey2,(100*sizeof(char)),IPC_CREAT|0666);
    if (mem_id<0) {
        perror("error shmget 2");
    }

    person->name=(char *)shmat(mem_id,(void*)0,0);
    if (person->name == (char *)(-1)) {
        perror("error shmat 2");
    }

    mem_id=shmget(mykey3,(10*sizeof(int)),IPC_CREAT|0666);
    if (mem_id<0) {
        perror("error shmget 3");
    }

    person->numbers=(int *)shmat(mem_id,(void*)0,0);
    if (person->numbers == (int *)(-1)) {
        perror("error shmat 3");
    }

    mem_id=shmget(mykey4,sizeof(Data),IPC_CREAT|0666);
    if (mem_id<0) {
        perror("error shmget 4");
    }
    data=(Data*)shmat(mem_id,(void*)0,0);
    if(data == (Data*)(-1)) {
        perror("error shmat 4");
    }

    mem_id=shmget(mykey5,(10*sizeof(int)),IPC_CREAT|0666);
    if (mem_id<0) {
        perror("error shmget 5");
    }

    data->persons=(Person *)shmat(mem_id,(void*)0,0);
    if (data->persons == (Person *)(-1)) {
        perror("error shmat 5");
    }    

    pid_t pid = fork(); 
    if (pid == 0) {
        person->name="bob";
        for(int i=0; i<10; i++) {
            person->numbers[i]=i;       
        }
        data->persons[0]=*person;
        data->persons[1].name="alice";
        printf("CHILD child wrote %s
", person->name);
        exit(0);
    }
    else {
        int status;
        waitpid(pid, &status, 0);
        printf("PARENT child wrote %s
", person->name);
        for(int i=0; i<10; i++) {
            printf("%d
",person->numbers[i]);      
        }

        printf("PARENT child wrote %s
", data->persons[0].name);
        for(int i=0; i<10; i++) {
            printf("%d
",data->persons[0].numbers[i]);     
        }
        printf("PARENT child wrote %s
", data->persons[1].name);
    }
    shmdt(person);
    shmdt(data);
    shmctl(mem_id,IPC_RMID,0); 

    return 0;
}

以上是关于与struct和malloc共享内存fork的主要内容,如果未能解决你的问题,请参考以下文章

linux vfork的子程序与父进程共享内存,那为啥子进程执行exec就不会覆盖父进程呢?

多进程多线程和相关同步机制

内存管理概述内存分配与释放地址映射机制(mm_struct, vm_area_struct)malloc/free 的实现

具体来说,fork() 如何处理 Linux 中 malloc() 动态分配的内存?

new与malloc的区别struct与class的区别

new与malloc的区别struct与class的区别