splunk
splunk

Reputation: 6815

Pass data from parent process to its child process — IPC, UNIX

I'm trying to create a program in C for Unix. Given an array I should pass every item of the array from parent process to its child through shared memory and using a semaphore. This is what I've got.

const key_t sem_key = (key_t)0x12345611;
const key_t shm_key = (key_t)0x12339611;

int sem_wait(int semafor_id)
{
    struct sembuf sb;
    sb.sem_num = 0;
    sb.sem_op = -1;
    sb.sem_flg = SEM_UNDO;

    return semop(semafor_id, &sb, 1);
}

int sem_signal(int semafor_id)
{
    struct sembuf sb;
    sb.sem_num = 0;
    sb.sem_op = 1;
    sb.sem_flg = SEM_UNDO;

    return semop(semafor_id, &sb, 1);
}

int main(int argc, char *argv[])
{
    int status;
    char str[3];
    str[0] = 'z';
    str[1] = 'x';
    str[2] = 'y';

    // Create shared memory
    int memory_id = shmget(shm_key, 1, 0600 | IPC_CREAT | IPC_EXCL);
    if (memory_id < 0)
    {
        printf("Shared memory creating failed\n");
        return 1;
    }

    // Create semafor
    int semafor_id = semget(sem_key, 10, 0600 | IPC_CREAT | IPC_EXCL);
    if (semafor_id < 0)
    {
        printf("SEMAFOR creating failed\n");
        return 1;
    }
    semctl(semafor_id, 0, SETVAL, 1); // init semafor

    pid_t pid = fork();
    if (pid < 0)
        printf("FORK FAILED\n");

    else if (pid > 0)
    {
        printf("PARENT\n");
        void *address = shmat(memory_id, NULL, 0);
        if (address == NULL)
        {
            printf("Failed to attach memory\n");
            return 1;
        }
        if (sem_wait(semafor_id) < 0) // sem_wait() decrements (locks) the semaphore pointed to by sem.
        {
            printf("Failed wait parent\n");
            return 1;
        }

        for (int i = 0; i < 3; i++)
        {
            void *address = shmat(memory_id, NULL, 0);
            if (address == NULL)
            {
                printf("Failed to attach memory\n");
                return 1;
            }
            printf("writer started.\n");
            char *data = (char *) address;
            *data = str[i];
            printf("writer ended.\n");
        }

        if (shmdt(address) != 0)
            printf("Failed to detach shared memory\n");

        if (sem_signal(semafor_id) < 0)
            printf("Failed signal parent\n");
        else
            printf("Parent leave generating number\n");


        // wait for child
        wait(&status);
        printf("Destroy semafor\n");
        semctl(semafor_id, 0, IPC_RMID, 0);
        printf("Destroy shared memory\n");
        shmctl(memory_id, IPC_RMID, 0);

        return 0;
    } // close parent case
    else
    {
        printf("CHILD\n");
        if (sem_wait(semafor_id) < 0)
            printf("FAILED wait child\n");
        else
        {
            void *address = shmat(memory_id, NULL, 0);
            if (address == NULL)
            {
                printf("Failed to atach memory in child\n");
                return 1;
            }
            char *data = (char *) address;
            printf("Child read data %c\n", *data);

            if (shmdt(address) != 0)
                printf("Failed to detach shared memory in child\n");
        }
    }

    if (sem_signal(semafor_id) < 0)
        printf("Failed signal child\n");
    else
        printf("Leave data reading\n");

    return 0;
}

The current output is this:

writer started
writer ended
writer started
writer ended
writer started
writer ended
child read data y
destroy semafor
destroy shared memory 

As you can see the parent write in the shared memory 3 times and then the child get the access of the shared memory printing the data of the shared memory. The output I'd like to have is this:

writer started
writer ended
child read data z
writer started
writer ended
child read data x 
writer started
writer ended
child read data y
destroy semafor
destroy shared memory  

Upvotes: 1

Views: 612

Answers (1)

fukanchik
fukanchik

Reputation: 2865

The problem is in the logic. After removing boilerplate code, the logic is as follows:

Get shared memory
Get semaphore with value 1
fork
in parent:
    wait semaphore (problem #1, #2) <-- supposed to decrement it to 0

    for(i=0;i<3;++i)
    {
        write next char <<-- no synchronization here :-(
    }

    signal semaphore 

    wait for child
    STOP

in child:
    wait semaphore (problem #1)
    read next char
    signal semaphore
    (problem #3)
    STOP

in english:

the writer acquires write lock
writes 3 values into the same location
then signals the reader to read data.
At this moment writer moves on to wait for child PID to die.

The reader reads the last value once
signals the semaphore
then exits.

At this moment writer sees reader died
cleans up
...and exits

Problem #1: parent and child processes run concurrently at the same time. Any of them can first succeed at sem_wait. If child does that first it would print out gibberish initial uninitialized value from shared memory and parent would be locked waiting for semaphore forever. Try adding imaginary (or not) sleep(1) into parent before wait semaphore. This will emulate processor context switch.

Problem #2: You wait/signal only once. E.g. you write all the values and only then signal. That is why only the last value is printed.

Problem #3: Child does not have any loops. How is he supposed to read 3 values?!

The logic should be:

Writer
    Do 3 times:
        acquire write semaphore
        write next value
        signal reader semaphore
    wait child to die
    clean up
    exit

reader:
    Do 3 times:
       acquire reader semaphore
       read next value
       signal writer semaphore
    exit

Upvotes: 2

Related Questions