Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shared memory fork with struct and malloc

I have a problem with the shared memory between parent and child processes generated by a fork. I know how to use shared memory with primitive type as described here.

While I do not know how to share struct that contains a pointer that can be allocated via malloc.

For instance, suppose that I have the following code took from the previous link with some modifications.

#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\n", (*(Data *)ptr).name);
        exit(0);
    }
    else {
        int status;
        waitpid(pid, &status, 0);
        //printf("child wrote %#lx\n", *(u_long *) ptr);
        printf("PARENT child wrote %s\n", (*(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;
}

I would like to change the data inside the struct between parent and child processes in order to allow IPC. How can I do?

Thank you

like image 827
erotavlas Avatar asked Mar 03 '18 17:03

erotavlas


People also ask

Does a forked process share memory?

After fork the parent and child can update their own copies of the variables in their own way, since they dont actually share the variable. Here we show how they can share memory, so that when one updates it, the other can see the change.

Do parent and child processes share memory?

Answer: Only the shared memory segments are shared between the parent process and the newly forked child process. Copies of the stack and the heap are made for the newly created process.

How parent and child processes use shared memory and address space?

You need the parent to ask for the number, write it into shared memory, and tell the child via some (other) IPC mechanism that the number is ready. The child should then wake up, read the number, do the calculation, and write the answer, then notify the parent it is done simply by exiting.

How do you share data between parent and child process?

Sharing data between Processes in Windows: Named Pipe and Shared Memory. Shared data is a fast way to communicate between parent and child processes. Depending on the size of the shared data, you can choose either named pipe or named shared memory.


2 Answers

If you're asking "how to do it with malloc()", it is not possible, unless you write your own malloc() implementation that allocated from a buffer in a shared memory region. That is a possible approach, but goes beyond this answer. Otherwise the memory allocated by malloc() is not shared between parent and child; each one gets it's own copy.

If you're asking "how to change the example so that it works" - simply use the memory returned by the mmap() call.

To simplify, let's declare name as an array in the Data structure:

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

Then the main code becomes:

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\n", (*(Data *)ptr).name);
    exit(0);
}
else {
    int status;
    waitpid(pid, &status, 0);
    printf("PARENT child wrote %s\n", (*(Data *)ptr).name);
}

Output:

CHILD child wrote bob

PARENT child wrote bob

like image 141
kfx Avatar answered Oct 06 '22 20:10

kfx


I solved by myself by following this guide and by using shmget, shmat, smhdt and shmctl instead of shm_open, mmap, munmap and shm_unlink.

In this way, I can manage dynamic shared memory as with malloc with the advantage that the memory is visible between the processes.

I report here the code as future hint.

#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\n", person->name);
        exit(0);
    }
    else {
        int status;
        waitpid(pid, &status, 0);
        printf("PARENT child wrote %s\n", person->name);
        for(int i=0; i<10; i++) {
            printf("%d\n",person->numbers[i]);      
        }

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

    return 0;
}
like image 38
erotavlas Avatar answered Oct 06 '22 21:10

erotavlas