0
votes

So I am trying to understand how semaphores with forks. I have written a C program which forks once and passes an integer, via a pipe, from the child pid to the parent pid 4 times. So I have been able to successfully read/write with a pipe between the forked children. However, I run into problems when I attempt to loop multiple writes to my pipe. I am attempting to regulate my pipe with semaphores in order to obtain alternating write, read, write, read, etc. My code with semaphores now generates SEGFAULTS or just hangs after printing out my header. I know I can accomplish this regulation with another pipe but I am specifically doing this to better understand semaphores. I have been reading an OS book but it is not really clicking; can anyone explain this to me via my code example? (This is not Homework)

/*****PROTOTYPES*****/
int SpawnBots();
void sender(int fd[2], sem_t *sem);
void receiver(int fd[2], sem_t *sem);

int exp(int base, int exponent);

int main(int argc, char *argv[]) {
    printf("WELCOME...\n");
    SpawnBots();

    return 0;
}


int SpawnBots() {
    sem_t *sem = sem_open("test_semaphore", O_CREAT | O_EXCL, 1, 1);
    sem_unlink("test_semaphore");
    int fd[2];              //0=input   1=output
    pid_t pid;

    //sem_init(sem, 1, 1);
    pipe(fd);
    pid = fork();

    if(pid==0) {
        //pid=0 is child
        sender(fd, sem);
    } else {
        //pid!=0 is parent
        receiver(fd, sem);
    }

    return 0;
}


void sender(int fd[2], sem_t *sem) {
    //child closes read pipe
    fflush(stdout);
    close(fd[0]);

    int val;
    char buffer[32];

    //writes 4 numbers to stdout
    for(int i=1; i<=4; i++) {
        sem_wait(sem);      //takes control; waits for read from receiver()

        val = exp((i+3), i);
        printf("\tChild %d: \t%d\n", i, val);       //prints out current data its writing
        sprintf(buffer, "%d", val);
        write(fd[1], buffer, strlen(buffer) + 1);   //writes to stdout

        sem_post(sem);      //hands back control
    }

    close(fd[1]);
    sem_close(sem);
    exit(0);
}


void receiver(int fd[2], sem_t *sem) {
    //child closes write pipe
    //fflush(stdout);
    close(fd[1]);

    int val;
    int i=1;
    int nbytes=1;
    char buffer[32];

    //reads in numbers until stdout is empty
    while(1) {
        sem_wait(sem);      //takes control; waits for write from sender()

        if((nbytes = read(fd[0], buffer, sizeof(buffer)+1)) > 0) {      //reads from stdout
            val = atoi(buffer);
            printf("\tParent %d: \t%d\n", i, val);      //prints out current data its reading
            i++;
        } else {
            break;
        }

        sem_post(sem);      //hands back control
    }

    sem_post(sem);
    close(fd[0]);
    sem_close(sem);
    exit(0);
}


int exp(int base, int exponent) {
    int output = 1;

    for(int i=1; i<=exponent; i++) {
        output = output * base;
    }

    return output;
}
1

1 Answers

0
votes

Both parent and child processes race to sem_wait on that semaphore, which has value of 1. If the parent wins, it blocks in read call. Whereas the child remains blocked in sem_wait. This is a deadlock.

One way to fix this deadlock is to create the semaphore with value of 2.

Another way is to remove sem_wait(sem) from receiver and sem_post(sem) from sender. This is because receiver blocks in read anyway until sender writes something into the pipe.