JMU
Semaphores
An Introduction


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Review
Some Other Coordination Mechanisms
Basics
What Does A Semaphore Mean?
Why Use Semaphores?
Types of POSIX Semaphores
Opening a Named Semaphore: sem_open() sem_open
sem_t *sem_open(const char *name, int oflag, /* mode_t mode, unsigned value */)
Purpose:
Initialize and open a named semaphore
Details:
name The name of the semaphore
oflag A flag that controls the opening (see below)
mode O_RDONLY, O_WRONLY, or O_RDWR (required iff O_CREAT is set)
value Required only O_CREAT is set
Return The handle for the semaphore on success; SEM_FAILED on error
#include <semaphore.h> semaphore.h

O_CREAT is used to create a semaphore. O_CREAT & O_EXCL is used to create a semaphore but fail if it already exists.

Semaphore Names
Semaphore Handles
Closing a Semaphore: sem_close() sem_close
int sem_close(sem_t *sem)
Purpose:
Terminate the association between a semaphore and a process (releasing any resources and and decreasing the reference count)
Details:
sem The handle to the semaphore
Return 0 on success; -1 on error
#include <semaphore.h> semaphore.h

Note that sem_close() does not delete the semaphore.

Deleting a Named Semaphore: sem_unlink() sem_unlink
int sem_unlink(const char *name)
Purpose:
Removes a semaphore and marks it for destruction (once all processes close it)
Details:
name The name of the semaphore
Return 0 on success; -1 on error
#include <semaphore.h> semaphore.h
Persistence of Named Semaphores
Decrement Operation: sem_wait() sem_wait
int sem_wait(sem_t *sem)
Purpose:
Attempt to decrease the value of a semaphore by 1 (blocking until it is possible to do so)
Details:
sem A pointer to the semaphore
Return 0 on success; -1 on error
#include <semaphore.h> semaphore.h
Decrement Operation (cont.)
Variants of sem_wait()
Increment Operation: sem_post() sem_post
int sem_post(sem_t *sem)
Purpose:
Increase the value of a semaphore by 1 (causing one process that is blocking to stop doing so)
Details:
sem A pointer to the semaphore
Return 0 on success; -1 on error
#include <semaphore.h> semaphore.h
Building Programs which Use Semaphores
Semaphores for Sequencing
unixexamples/semaphores/sequence.c
        #include <fcntl.h>       // For O_CREAT
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main(void)
{
  pid_t  pid;
  sem_t *done;

  // Call open() in the original process so that we don't have to
  // worry about coordinating the call to open() 
  done = sem_open("/done", O_CREAT, O_RDWR, 0);
  
  pid = fork();
  switch(pid)
    {
    case -1:
      exit(1);

    case 0:  // Child
      write(STDOUT_FILENO, "Task 1\n", 7);
      sem_post(done);    // Increment the semaphore

      sem_close(done);
      exit(0);

    default: // Parent
      sem_wait(done);    // Try to decrement the semaphore
      write(STDOUT_FILENO, "Task 2\n", 7);

      sem_close(done);
      sem_unlink("/done");
      write(STDOUT_FILENO, "\nCheck: ls -l /dev/shm/sem.* \n", 30);
      exit(0);
    }
}
        
An Example of "Alerting" with a Semaphore
unixexamples/semaphores/taxs1.c
        #include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../signals/tax_lib.h"

int
main(void)
{
  float rate, sales, tax;
  int   should, status;
  pid_t pid;
  sem_t *alert;

  rate  = 0.05;

  alert = sem_open("/alert", O_CREAT, O_RDWR, 0);

  pid   = fork();
  switch(pid)
    {
    case -1:
      exit(1);

    case 0:
      if (should_tax()) sem_post(alert); // Increment (i.e., alert the child)
      sem_close(alert);
      exit(0);

    default:
      sales = total_sales();

      // Reap the child (waiting for it to finish)
      wait(&status);

      // See whether the child "alerted". This function will return -1
      // if the semaphore can't be decremented immediately
      should = sem_trywait(alert);

      if (should == -1) tax = 0.0;            // The child didn't "alert"
      else              tax = sales * rate;   // The child "alert"

      printf("Tax: %5.2f\n", tax);

      sem_close(alert);
      sem_unlink("/alert");
      exit(0);
    }
}
        

Note: The "sender" transmits one bit of information (as in the similar examples that used signals and pipes) by either incrementing the semaphore or not.

Complicating the "Alerting" Example
Complicating the "Alerting" Problem (cont.)
unixexamples/semaphores/taxs2.c
        #include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../signals/tax_lib.h"

int
main(void)
{
  float rate, sales, tax;
  int   should, status;
  pid_t pid;
  sem_t *alert, *done;

  rate  = 0.05;

  alert = sem_open("/alert", O_CREAT, O_RDWR, 0);
  done  = sem_open("/done",  O_CREAT, O_RDWR, 0);
  
  pid   = fork();
  switch(pid)
    {
    case -1:
      exit(1);

    case 0:
      // "Alert" the child if necessary
      if (should_tax()) sem_post(alert);

      // In either case, indicate that the calculation is done
      sem_post(done);

      record_status(); // This takes a long time

      // Close the semaphores
      sem_close(alert);
      sem_close(done);
      exit(0);

    default:
      sales = total_sales();

      // Try to decrement the semaphore and wait (until the child is done)
      // if it isn't possible
      sem_wait(done); 
      
      // See whether the child "alerted". This function will return -1
      // if the semaphore can't be decremented immediately
      should = sem_trywait(alert);

      if (should == -1) tax = 0.0;            // The child didn't "alert"
      else              tax = sales * rate;   // The child "alert"

      printf("Tax: %5.2f\n", tax);


      // Reap the child (waiting for it to finish)
      wait(&status);

      // Close and unlink the semaphores
      sem_close(alert);
      sem_unlink("/alert");
      sem_close(done);
      sem_unlink("/done");
      
      exit(0);
    }
}
        

Note: One semaphore is being used to satisfy the sequencing constraint and another is being used for "alerting".

Semaphores for Satisfying Mutual Exclusion
unixexamples/semaphores/mutex.c
        #include <fcntl.h>       // For O_CREAT
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main(void)
{
  pid_t  pid;
  sem_t *conch;

  // The semaphore is named conch in a reference to "Lord of the Flies"
  // by William Golding (you had to have the conch to talk)
  conch = sem_open("/conch", O_CREAT, O_RDWR, 1); // The initial value is 1
  
  pid = fork();
  switch(pid)
    {
    case -1:
      exit(1);

    case 0:  // Child
      sem_wait(conch);  // Try to decrement the semaphore
      // Start of Critical Section
      write(STDOUT_FILENO, "Ralph \n", 7);
      // End of Critical Section
      sem_post(conch);  // Increment the semaphore

      sem_close(conch);
      exit(0);

    default: // Parent
      sem_wait(conch);  // Try to decrement the semaphore
      // Start of Critical Section
      write(STDOUT_FILENO, "Piggy \n", 7);
      // End of Critical Section
      sem_post(conch);  // Increment the semaphore

      sem_close(conch);
      sem_unlink("/conch");
      write(STDOUT_FILENO, "\nCheck: ls -l /dev/shm/sem.* \n", 30);
      exit(0);
    }
}
        

Note: The semaphore is initialized to 1 so that whichever process checks first can "enter".