简体   繁体   中英

Why a child process fails to write to a shared memory?

I have a simple program that writes to a shared memory and reads from a shared memory, but while reading I am getting segmentation fault.

When I am debugging, the child process is not writing information to the shared memory, and after that, the parent process is trying to read from the shared memory which has no data and is throwing segmentation fault at 1st printf, in parent printf("%d\\n",ptr->nread);

Why the child process is not able to write data to the shared memory? (it is failing at ptr->nread=20; line)

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define  SIZE 5*1024
struct databuf{
    int nread;
    char *buf;
    int xyz;
};
    struct databuf* ptr;
main()
{
    int shmid,pid;
    shmid = shmget((key_t)1,SIZE,IPC_CREAT|0777);
    pid =  fork();
    if(pid==0)
    {
            ptr = (struct databuf *)shmat(shmid,(char*)0,0);
            ptr->nread=20;
            ptr->buf=ptr+sizeof(ptr->nread);
            strcpy(ptr->buf, "abc");
            ptr->xyz=20;
    }
    else
    {
        wait(0);
        ptr = (struct databuf *)shmat(shmid,(char*)0,0);
        printf("%d\n",ptr->nread);
        printf("%s\n",ptr->buf);
        printf("%d\n",ptr->xyz);
    }
    return 0;
}

If ptr->nread is failing then you should put a error checking code something like this, before acessing the ptr .

 ptr = (struct databuf *)shmat(shmid,(char*)0,0);
 if (data == (struct databuf *)(-1)) {
                perror("shmat failed");
                exit(1);
 }
 ptr->nread=20;

Ref: http://linux.die.net/man/2/shmat

ptr->buf=ptr+sizeof(ptr->nread);

could be written as:

 ptr->buf=(char*)ptr+sizeof(struct databuf)+ptr->nread;

or

 ptr->buf=(char*)ptr+ptr->nread;

The string can now be accessed in the parent process.

Brief Explanation: If you're using shared memory, you have to make sure all the data you want to access in other processes is in the shared memory segment. Leave the data at a specified offset(in your case ptr+ptr->nread ) in the memory segment. And be careful not to overwrite the existing data in the shared memory. sizeof(ptr->nread) will yield the sizeof(int).

Leaving to one side all the other issues with the code, I think:

shmid = shmget((key_t)1, SIZE, IPC_CREAT|0777) ;

is probably a mistake, unless you can (somehow) guarantee that (key_t)1 is not in use elsewhere. For parent-child communication, as in this case, the simpler approach is:

shmid = shmget((IPC_PRIVATE, SIZE, IPC_CREAT|0777) ;

In general, when something apparently inexplicable happens, I find it helps to make sure I have checked for error returns. In this case: shmget() returning -1 or shmat() returning -1 ... and I suspect that both have happened.


Worked solution added by @WhozCraig:

The following example works , and likely does what you're trying to accomplish. Pay note to how we calculate the address we store in ptr->buf after we home ptr to our shared memory base address. We have to leave room for the structure, so we calculate the address to start the first byte past the structure back-side.

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

struct databuf
{
    int nread;
    char *buf;
    int xyz;
};

#define SIZE (5*1024)

int main()
{
    // acquire shared memory first (read/write access)
    int shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0666);
    if (shmid == -1)
    {
        perror("Failed to acquire shared emory.");
        exit(EXIT_FAILURE);
    }

    // fork child process
    pid_t pid = fork();

    // both parent and child need this. may as well do both before
    //  special casing child vs. parent logic.
    struct databuf *ptr = shmat(shmid,(char*)0,0);
    if (ptr == (void*)(-1))
    {
        perror("Failed to map shared memory to our process");
        exit(EXIT_FAILURE);
    }

    // child process
    if (pid==0)
    {
        ptr->nread = 20;
        ptr->buf = ((char*)ptr) + sizeof(*ptr);
        strcpy(ptr->buf, "abc");
        ptr->xyz = 30;
    }

    // parent process
    else
    {
        wait(NULL);
        printf("ptr = %p, ptr->buf = %p\n", ptr, ptr->buf);
        printf("%d\n",ptr->nread);
        printf("%s\n",ptr->buf);
        printf("%d\n",ptr->xyz);
    }
    return 0;
}

Sample Output (varies by system obviously)

ptr = 0x80000, ptr->buf = 0x8000c
20
abc
30

it is worth noting that this:

ptr->buf = ((char*)ptr) + sizeof(*ptr); 

could be written as the following, using typed-pointer math:

ptr->buf = (char*)(ptr + 1);

The location where the cast is applied is important. The first applies it before we do any math, so we need to account for octet count. The second applies it after the pointer math, so simply adding one to to the typed pointer ptr will move us to the memory immediately past our ptr structure base.

Best of luck.

If you have a pointer from type T, an addition by one to the pointer will increment it by sizeof(T).

So you have to replace:

ptr->buf=ptr+sizeof(ptr->nread);

to

ptr->buf= ((char*)ptr )+sizeof(ptr->nread);

If you don't do that, your pointer will be incremented by sizeof(T)^2.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM