Search code examples
linuxposixipcsemaphore

POSIX implementation of a named semaphore for IPC


I am working on a homework assignment involving implementing a semaphore to enforce mutual exclusion between child processes. I have most of the code working, except that I am not using the semaphore correctly. The articles I have found aren't helping much. Could someone explain to me how the POSIX semaphore works?

For example, If i had a parent process spawn child processes using fork() and execl():

sem=sem_open("/semaphore1",O_CREAT|O_EXCL,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,1);
for (i = 0; i < 3; i++) 
    {
        //three child process are spawned in the image of the parent
        child[i] = fork();

        //establish whether all children were created successfully
        switch (child[i]) 
        {
            //child process creation failed...
            case -1:
                rpterror ((char *)"fork failure", pname);
                exit(1);
            //given that the fork() was successful (the children were spawned successfully)...
            case 0:
                sprintf (pname, "shmc%d", i+1);
                execl("shmc1", pname, ascshmid, (char *)0);
                perror ("execl failed");
                exit (2);
        }
    }

and the children wanted to access and modify a value in a shared memory segment (created by the parent):

sem=sem_open("/semaphore1", O_RDWR);
while ( !all_out) 
{       /* loop to sell all seats */

    /* puts the process to sleep for an amount of time, then decreases the amount of seats available. Before printing out the new count of seats, the process sleeps again. Finally, it prints the seat count until there are no more seats left.*/
    if (class_ptr->seats_left > 0) 
    {
        sem_wait(sem);
        sleep ( (unsigned)rand()%5 + 1);
        class_ptr->seats_left--;
        sleep ( (unsigned)rand()%5 + 1);
        cout << pname << " SOLD SEAT -- "  << class_ptr->seats_left << " left" <<endl;
        sem_post(sem);
    }

    else
    {
        all_out++;
        cout << pname << " sees no seats left" << endl;
    }
    sleep ( (unsigned)rand()%10 + 1);
}

where seats_left is the shared variable.

running this code gives me an output that looks like this. The shared variable has an initial value of 15:

shmc1 SOLD SEAT -- 14 left
shmc2 SOLD SEAT -- 13 left
shmc3 SOLD SEAT -- 12 left
shmc1 SOLD SEAT -- 11 left
shmc2 SOLD SEAT -- 10 left
shmc3 SOLD SEAT -- 9 left
shmc1 SOLD SEAT -- 8 left
shmc2 SOLD SEAT -- 7 left
shmc3 SOLD SEAT -- 6 left
shmc2 SOLD SEAT -- 5 left
shmc1 SOLD SEAT -- 4 left
shmc3 SOLD SEAT -- 3 left
shmc2 SOLD SEAT -- 2 left
shmc1 SOLD SEAT -- 1 left
shmc1 sees no seats left
shmc3 SOLD SEAT -- 0 left
shmc3 sees no seats left
shmc2 SOLD SEAT -- -1 left
shmc2 sees no seats left
Parent removing shm

As you can see, towards the end is where my processes enter critical section at the same time another process is doing so. Does anyone have any idea why that is?


Solution

  • Try moving the sem_wait() outside of the if statement:

    sem=sem_open("/semaphore1", O_RDWR);
    while (!all_out) {
      sem_wait(sem);
      if (class_ptr->seats_left > 0)  {
        sleep((unsigned)rand()%5 + 1);
        class_ptr->seats_left--;
        sleep((unsigned)rand()%5 + 1);
        cout << pname << " SOLD SEAT -- "  << class_ptr->seats_left << " left" <<endl;
      }
      else {
        all_out++;
        cout << pname << " sees no seats left" << endl;
      }
      sem_post(sem);
      sleep((unsigned)rand()%10 + 1);
    }
    

    I don't think you have an issue with processes not respecting the critical segment (although semaphores are sort of an honor system, just like regular stop lights..). I think the problem is just that process B is waiting on the lock, which is held by process A, and when A sells the last ticket and relases the lock, B grabs the lock and sells another ticket, because it already checked if tickets were available and never checks again before selling that last one.

    If you run this enough times, you'll probably see instances of zero, one and two tickets being oversold.