0

it's my first time posting here. I'm new to OS programming.

So I have this program that I found online and decided to modify it. The parent creates a char array that contains a random letter. This letter is passed to the child, and the child removes it.

I have trouble retrieving the letter from the child. I'm getting something, but they're not the correct letters.

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

#define BUFFER_SIZE 10

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
#else

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
    struct seminfo *__buf;
};
#endif

int main(int argc, char* argv[])
{
    int sem_set_id;
    union semun sem_val;
    int child_pid;
    int i, j = 0, k;
    struct sembuf sem_op;
    int rc;
    struct timespec delay;

    srand(time(NULL));

    sem_set_id = semget(IPC_PRIVATE, 1, 0600);
    if (sem_set_id == -1)
    {
        perror("main: semget");
        exit(1);
    }
    printf("semaphore set created, semaphore set id '%d'.\n", sem_set_id);

    sem_val.val = 0;
    rc = semctl(sem_set_id, 0, SETVAL, sem_val);

    child_pid = fork();

    char letter[BUFFER_SIZE];

    int fd[2];

    switch (child_pid)
    {
        case -1:
            //fail
            perror("fork");
            exit(1);
        case 0: //child
            close(fd[1]);

            for (i = 0; i < BUFFER_SIZE; i++)
            {
                sem_op.sem_num = 0;
                sem_op.sem_op = -1;
                sem_op.sem_flg = 0;
                semop(sem_set_id, &sem_op, 1);

                read(fd[0], &letter[i], sizeof(letter[i]));

                printf("Consumer removes ");
                printf("['%c'] ", letter[i]);
                printf("at index [%d]\n", i);

                printf("\n");
                fflush(stdout);

                close(fd[1]);
            }
            break;
        default: //parent
            close(fd[0]);

            for (i = 0; i < BUFFER_SIZE; i++)
            {
                //usleep(100);

                letter[i] = (char)num(i);
                write(fd[1], &letter[i], sizeof(letter[i]));

                printf("Producer creates ");
                printf("['%c'] ", letter[i]);
                printf("at index [%d]\n", i);

                printf("\n");
                fflush(stdout);

                sem_op.sem_num = 0;
                sem_op.sem_op = 1;
                sem_op.sem_flg = 0;
                semop(sem_set_id, &sem_op, 1);

                if (rand() > 3 *(RAND_MAX/4))
                {
                    delay.tv_sec = 0;
                    delay.tv_nsec = 10;
                    nanosleep(&delay, NULL);
                }
                close(fd[1]);
            }
            break;
    }

    return 0;
}

int num(int i)
{   
    i = rand() % (90 - 65) + 65;

    return i;
}

Here is a sample output:

semaphore set created, semaphore set id '2850903'.
Producer creates ['N'] at index [0]

Producer creates ['M'] at index [1]

Consumer removes ['0'] at index [0]

Consumer removes ['P'] at index [1]

Producer creates ['E'] at index [2]

Producer creates ['I'] at index [3]

Producer creates ['X'] at index [4]

Consumer removes [''] at index [2]

Consumer removes ['�'] at index [3]

Consumer removes ['�'] at index [4]

Producer creates ['Q'] at index [5]

Consumer removes [''] at index [5]

Producer creates ['M'] at index [6]

Consumer removes [''] at index [6]

Producer creates ['F'] at index [7]

Consumer removes [''] at index [7]

Producer creates ['M'] at index [8]

Producer creates ['D'] at index [9]

Consumer removes [''] at index [8]

Consumer removes [''] at index [9]

EDIT1 I used shared memory. The child only recognizes the last letter that the parent added on the array and repeats it.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/shm.h>

#define BUFFER_SIZE 10

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
#else

union semun
{
        int val;
        struct semid_ds *buf;
        unsigned short int *array;
        struct seminfo *__buf;
};
#endif

int main(int argc, char* argv[])
{
    char letter[BUFFER_SIZE];

    int shmid;

    void *shm = NULL;

    shmid = shmget((key_t)1, sizeof(int), 0666|IPC_CREAT);
    shm = shmat(shmid, 0, 0);

    int *shared = (int*)shm;

    *shared = 0;

    int sem_set_id;
    union semun sem_val;
    int child_pid;
    int i, j = 0, k;
    struct sembuf sem_op;
    int rc;
    struct timespec delay;

    srand(time(NULL));

    sem_set_id = semget(IPC_PRIVATE, 1, 0600);
    if (sem_set_id == -1)
    {
        perror("main: semget");
        exit(1);
    }
    printf("semaphore set created, semaphore set id '%d'.\n", sem_set_id);

    sem_val.val = 0;
    rc = semctl(sem_set_id, 0, SETVAL, sem_val);

    child_pid = fork();

    switch (child_pid)
    {
        case -1:
            //fail
            perror("fork");
            exit(1);
        case 0: //child

            for (i = 0; i < BUFFER_SIZE; i++)
            {
                sem_op.sem_num = 0;
                sem_op.sem_op = -1;
                sem_op.sem_flg = 0;
                semop(sem_set_id, &sem_op, 1);

                printf("Consumer removes ");
                printf("['%c'] ", *shared);
                printf("at index [%d]\n", i);

                printf("\n");
                fflush(stdout);
            }
            break;
        default: //parent
            for (i = 0; i < BUFFER_SIZE; i++)
            {
                //usleep(100);
                shm = shmat(shmid, 0, 0);
                *shared = num(i);

                printf("Producer creates ");
                printf("['%c'] ", *shared);
                printf("at index [%d]\n", i);
                printf("\n");
                fflush(stdout);

                sem_op.sem_num = 0;
                sem_op.sem_op = 1;
                sem_op.sem_flg = 0;
                semop(sem_set_id, &sem_op, 1);

                if (rand() > 3 *(RAND_MAX/4))
                {
                    delay.tv_sec = 0;
                    delay.tv_nsec = 10;
                    nanosleep(&delay, NULL);
                }
            }
            break;
    }

    return 0;
}

int num(int i)
{   
    i = rand() % (90 - 65) + 65;

    return i;
}
schnitzcel
  • 17
  • 5

1 Answers1

3

When you use fork, each variable is duplicated to each process - ie each process has its own copy of already defined variables-, and new variables are process dependent. So, you cannot simply shared memory like that.

In your case, your write in an array and read in another one, which is not initialized, because each process (child and parent) has its own memory.

Take a look at : How to share memory between process fork()? It would explain to you how to use shared memory between processes created with fork (shm_open(), shm_unlink(), mmap(), etc.).

EDIT

By reading your code, title and question, you mix 2 way to share data between child and parent. So you have 2 choices :

  • Use shared memory and semaphore : array are shared between process, and semaphore ensure you that you can read or write some part of shared array. No need of read, write and fd
  • Or use a pipe : don't need shared memory and semaphore, just use a pipe, ie fd read and write.

Choice 1

The first argument of mmap is pretty same as malloc. So in your case with array you should have something like that :

...
char * glob_array = mmap(NULL, sizeof(char) * BUFFER_SIZE, PROT_READ | PROT_WRITE, 
                MAP_SHARED | MAP_ANONYMOUS, -1, 0);

child_pid = fork();

....

You can now use glob_array in both child and parent. In this case, don't use read or write and fd, data are directly available in glob_array (ie if you need first data, use glob_array[0] directly, glob_array[1] for second and so on). Also, semaphore ensure you about simultaneous read/write.

Choice 2

By re-reading your code, I think I pass over something : you maybe want to use read and write to pass values from child to parent. So you need pipe. On original code you pasted, move declaration of fd before fork and initialize pipe :

int fd[2];

if (pipe(pipefd) == -1) {       
    perror("pipe");
    exit(EXIT_FAILURE);
}

child_pid = fork();

And it will be work without sharing array ;) Also read in a pipe will block until a data is available, so, you don't need semaphore.

Community
  • 1
  • 1
Garf365
  • 3,619
  • 5
  • 29
  • 41
  • I figured out how the sharing works, but I'm still unsure how it works with arrays. For now, the child only detects the last letter that the parent added. Thanks for the link. – schnitzcel Mar 11 '16 at 10:16
  • @schnitzcel sorry I re-read your code, I think you would like using pipe instead of shared memory, it's right ? see my edit2 if it what you need, and if it is, I will totally remove part about shared memory – Garf365 Mar 11 '16 at 10:51
  • @schnitzcel Or a can leave both and adding choice – Garf365 Mar 11 '16 at 11:03
  • Thanks for helping me. I added how I used shared memory on my edit. I kind of want to use shared memory more than pipe. I tried the pipe, and it gave me the same output. – schnitzcel Mar 11 '16 at 11:10
  • @schnitzcel is the answer give you every information you need ? If it miss something, don't hesitate to ask here. And if everything is good, thanks to accept answer ;) – Garf365 Mar 11 '16 at 11:17
  • 1
    I think you gave me enough information. I'll take a crack at this and see where it goes. Thanks! – schnitzcel Mar 11 '16 at 11:40