Question

I compiled it on Linux with: g++ test.c -o test

I rewritten the original example. Now made the first process to wait 2 seconds, (so that process2 could write on the shared memory), then I made process1 to read from that memory. Is this test correct?

Secondo question: where should I put:

shmdt(tests[0]); // or 1
shmctl(statesid, IPC_RMID, 0); 

//Global scope
char *state[2];
//...
//...

struct teststruct {
  int stateid;
  teststruct *next;
  //other things
};

void write(teststruct &t, char* what)
{
  strcpy(state[t.next->stateid], what);   
  printf("\n\nI am (%d), I wrote on: %d", t.stateid, t.next->stateid); 
}

void read(teststruct &t)
{
  printf("\n\nI am (%d), I read: **%s**", t.stateid, state[t.stateid]);        
}


int main() {

  key_t key;
  if ((key = ftok(".", 'a')) == -1) {
    perror("ftok");
    exit(1);
  }

   int statesid;
  if ((statesid = shmget(key, sizeof(char*)*50, 0600 | IPC_CREAT )) == -1) {
    perror("shmget error");
    exit(1);
  }

  state[0] = (char*)shmat(statesid, NULL, 0);
  state[1] = (char*)shmat(statesid, NULL, 0);

  teststruct tests[2];
  tests[0].stateid = 0;
  tests[0].next = &tests[1];
  tests[1].stateid = 1;
  tests[1].next = &tests[0];

  int t0, t1;
  switch (t0 = fork()) {
  case (0):
    sleep(2);
    read(tests[0]);

    exit(0);

  case (-1):
    printf("\nError!");
    exit(-1);

  default:
    wait();
  }

   switch (t1 = fork()) {
  case (0):
    write(tests[1], "1 write on 0 in theory.");

    exit(0);

  case (-1):
    printf("\nError!");
    exit(-1);

  default:
    wait();
  }

  return 0;
}

In particular I am asking if "state" is really shared between the two process, and If what I've done is a good way to do that.

My goal is to make char *state[2] shared (reading/modifying) between the two processes after fork.

Was it helpful?

Solution

You don't need to call shmat() twice. You've only allocated enough space for two pointers, so you can't communicate much between the two processes. And you can't rely on being able to copy a pointer to memory in the first process into shared memory and then have the second process read and use it. The address may be valid in the first process and not in the second; it may well point at completely different data in the second process (dynamic memory allocation in particular could screw this up). You can only rely on the contents of the shared memory being the same in both processes. You should allocate enough shared memory to hold the shared data.

However, with that said, the two processes should be sharing that small piece of shared memory, and in both processes, state[0] and state[1] will point at the shared memory and you should be able to communicate between the two by writing in the shared memory. Note that after forking, if either process changes the value stored in its state[0] or state[1], the other process will not see that change — the other process can only see what changes in the shared memory those pointers point to.

Of course, you've not set up any synchronization mechanism, so the access will likely be chaotic.

How can I modify my code just to make it works as intended (without considering synchronization issues)?

It isn't entirely clear how it is intended to work, which complicates answering the question. However, if you want (for sake of example) the child process to write a word to the shared memory and the parent process to read the word from shared memory, then you allocate enough shared memory for the biggest word you're willing to process, then arrange for the child to copy a word from its per-process memory into the shared memory (and notify the parent that it has done so), and then the parent can copy or read the word from shared memory and compare it with data from its per-process memory.

Because you have a parent-child process which are forks of the same process, you will find that the two processes share a lot of the same memory addresses containing the same information. This is, however, coincidental. You can have unrelated processes connect to shared memory, and they need not have any addresses in common. Thus, it would be trivial to get spurious results from your current setup.


Working Code

For some definitions of 'working', the following C++ code does. The code is subtly C++; the code assumes struct teststruct declares type teststruct, and uses references as parameters.

Note that the (revised) code in the question has its wait() calls infelicitously placed.

shm2.cpp

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

static char *state = 0;

struct teststruct
{
    int stateid;
    teststruct *next;
};

void sm_write(teststruct &t, char* /*what*/)
{
    //strcpy(state[t.next->stateid], what);   
    printf("[%5d] I am (%d), I wrote on: %d\n", (int)getpid(), t.stateid, t.next->stateid); 
}

void sm_read(teststruct &t)
{
    printf("[%5d] I am (%d), I read: **%s**\n", (int)getpid(), t.stateid, state);        
}

int main(void)
{
    key_t key;
    if ((key = ftok(".", 'a')) == -1) {
        perror("ftok");
        exit(1);
    }

    int statesid;
    if ((statesid = shmget(key, sizeof(char)*512, 0600 | IPC_CREAT )) == -1) {
        perror("shmget error");
        exit(1);
    }

    if ((state = (char*)shmat(statesid, NULL, 0)) == 0)
    {
        perror("shmat");
        exit(1);
    }

    sprintf(state, "This is a string in shared memory %d", 919);

    teststruct tests[2];
    tests[0].stateid = 0;
    tests[0].next = &tests[1];
    tests[1].stateid = 0;
    tests[1].next = &tests[0];

    int t0, t1;
    if ((t0 = fork()) < 0)
    {
        perror("fork-1");
        exit(1);
    }
    else if (t0 == 0)
    {
            sm_read(tests[0]);
            printf("[%5d] sleeping\n", (int)getpid());
            sleep(2);
            printf("[%5d] waking\n", (int)getpid());
            sm_read(tests[0]);
            exit(0);
    }
    else if ((t1 = fork()) < 0)
    {
        perror("fork-2");
        exit(-1);
    }
    else if (t1 == 0)
    {
        printf("[%5d] sleeping\n", (int)getpid());
        sleep(1);
        printf("[%5d] waking\n", (int)getpid());
        strcpy(state, "1 write on 0 in theory.");
        sm_write(tests[1], state);
        exit(0);
    }

    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        printf("PID %5d died with status 0x%.4X\n", corpse, status);

    return 0;
}

Example run

[20440] sleeping
[20440] waking
[20440] I am (0), I wrote on: 0
[20439] I am (0), I read: **This is a string in shared memory 919**
[20439] sleeping
[20439] waking
[20439] I am (0), I read: **1 write on 0 in theory.**
PID 20440 died with status 0x0000
PID 20439 died with status 0x0000

OTHER TIPS

You have a problem with the size of the shared memory. In:

 (statesid = shmget(key, sizeof(char*)*2, 0600 | IPC_CREAT )

you are just reserving space for 2 pointers to char. You need to allocate enough space for all your data, that based on the struct is kind of linked structure. The code could be something like the following, though the purpose of the fork() and shared memory is not very clear to me:

struct teststruct {
    int stateid;
    teststruct *next;
    //other things
};

void dosomething(teststruct &t){
    //forget about global space, you don't need it
}

int main() {

    key_t key;
    if ((key = ftok(".", 'a')) == -1) {
        perror("ftok");
        exit(1);
    }

    int statesid;
    int size_struct = sizeof(teststruct)*2; //assuming you will have only 1 level of linking
    if ((statesid = shmget(key, size_struct, 0600 | IPC_CREAT )) == -1) {
        perror("shmget error");
        exit(1);
    }

    //if you need to hold just one teststruct object data, you can do
    teststruct* p_test_struct = (teststruct*)shmat(statesid, NULL, 0);

    for (int i=0; i<2; i++){
        *p_test_struct = tests[i]; //this actually writes tests[i] into shared mem
        int t0, t1;
        switch (t0 = fork()) {
        case (0):
            dosomething(*p_test_struct);
            exit(0);
        case (-1):
            printf("\nError!");
            exit(-1);
        default:
            wait();
        }
    }

    return 0;
}

No, it does not. Because you are using fork (multiprocess) instead of threads (multithread). Memory zones are not shared into parent and child process. You will have the same value into it on the child but after that it will be independent to the another one.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top