user1833028 user1833028 - 4 months ago 5
C Question

Why does my linked list in shared memory always lead to a segfault?

I have the following simple application. This has been stripped of error handling and such, to be, well, a minimal complete example.

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

//#define SHM_SIZE 1024 /* make it a 1K shared memory segment */

struct node
{
int x;
struct node *next;
};

int main(int argc, char *argv[])
{
struct node *root[argc+1];
if (argc > 1)
{
int i;
root[0]= (struct node *) malloc( sizeof(struct node) );
for (i=0; i<argc-1; i++)
{
root[i]->x = (int)(*argv[i+1]-'0');
//root[i]->next=&root[i]+sizeof(struct node);
root[i+1]=(struct node *) malloc( sizeof(struct node) ); //Okay, wastes a few ops
root[i]->next=root[i+1];
}
free(root[i]->next);
root[i]=NULL;
}

key_t key;
int shmid;
struct node *data;

key = ftok("test1", 'O');
shmid = shmget(key, (size_t)(sizeof(struct node)*1000), 0777 | IPC_CREAT);


data = shmat(shmid, (void *)0, 0);
printf("%p", &data);

if (argc != 1)
{
int z=0;
for (z=0;z<argc-1;z++){
*(data+sizeof(struct node)*z)=*root[z];
if (z) (data+sizeof (struct node)*(z-1))->next=(data+sizeof (struct node)*z);
}
(data+z)->next=0;

}

if (argc)
{
printf("This is the routine that will retrieve the linked list from shared memory when we are done.");
struct node *pointer;
pointer=data;
printf("%p", data);

while (pointer->next != 0)
{
printf("\n Data: %i",pointer->x);
pointer=pointer->next;
}
}
/* detach from the segment: */
if (shmdt(data) == -1)
{
perror("shmdt");
exit(1);
}

return 0;
}


Basically, whenever I try to access the shared memory from the process that created it, my output looks good. Every time I open the shared memory from a process that did NOT create it (argc=1) then the program segfaults. I would appreciate it if someone could tell me why!

Answer

Each time you attach a shared memory segment in a process, it is attached at some address which has no relation to the address in any other process that attaches the shared memory. So the pointers in the shared memory, while they point at other objects in the shared memory in the original (creating) process's shared memory, do not point at the shared memory in any other process.

The net result -- if you want to store data structures in the shared memory, those data structures can't contain any pointers if you want them to work reasonably. If you want pointer-like behavior, you instead need to use indexes into the shared momory, possibly as an array. So you could do something like:

struct node {
    int x;     /* value */
    int next;  /* index of the next node */
};


struct node *data = shmat(...);  /* shm was size for 1000 nodes */

for (int i = 0; i < 1000; ++i) {
    data[i].next = i+1;
}
data[999].next = -1;  /* using -1 for "NULL" as 0 is a valid index; */

for (int i = 0; i >= 0; i = data[i].next) {
    /* iterating down the linked list */
    data[i].x = 0;
}