Reputation: 512
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
Upvotes: 5
Views: 3116
Reputation: 512
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;
}
Upvotes: 2
Reputation: 8537
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
Upvotes: 7