简体   繁体   中英

How can I enforce order of operations between processes using semaphores?

I am currently playing around with semaphores and trying to understand them. I am attempting to follow a tutorial which asks me to edit example code to get a program to run two processes which take turns to output the lyrics to a song (' There's a hole in the bucket ').

My problem is that the processes do not alternate when I add more lines of the song in the program but when there is just two lines they work properly.

One process handles Liza's part, another Henry's part. This is my code:

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

#define KEY 87654 //Unique semaphore key

int main()
{
  int id; /* Number by which the semaphore is known within a program */

  union semun {
    int val;
    struct semid_ds *buf;
    ushort * array;
  } argument;

  argument.val = 1;

  /* Create the semaphore with external key KEY if it doesn't already 
     exists. Give permissions to the world. */
  id = semget(KEY, 1, 0666 | IPC_CREAT);

  /* Always check system returns. */      
  if(id < 0) {
      fprintf(stderr, "Unable to obtain semaphore.\n");
      exit(0);
  }

  /* What we actually get is an array of semaphores. The second 
     argument to semget() was the array dimension - in our case
     1. */

  /* Set the value of the number 0 semaphore in semaphore array
     # id to the value 0. */      
  if( semctl(id, 0, SETVAL, argument) < 0) {
      fprintf( stderr, "Cannot set semaphore value.\n");
  } else {
      fprintf(stderr, "Semaphore %d initialized.\n", KEY);
  }

  int pid=fork();

  if(pid) {
    struct sembuf operations[1];
    int retval; /* Return value from semop() */

    /* Get the index for the semaphore with external name KEY. */
    id = semget(KEY, 1, 0666);

    if(id < 0){
      /* Semaphore does not exist. */

      fprintf(stderr, "Program sema cannot find semaphore, exiting.\n");
      exit(0);
    }
    operations[0].sem_num = 0;
    /* Which operation? Subtract 1 from semaphore value : */
    operations[0].sem_op = -1;
    /* Set the flag so we will wait : */   
    operations[0].sem_flg = 0;

    while(1){
      //Process 1

      //wait
      operations[0].sem_op = -1;
      retval = semop(id, operations, 1);

      //critical section
      printf("Then mend it, dear Henry, dear Henry, dear Henry, \n");
      printf("Then mend it, dear Henry, dear Henry, mend it. \n");

      fflush(stdout);
      int stime=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stime);
      sleep(stime);

      printf("With a straw, dear Henry, dear Henry, dear Henry, \n");
      printf("With a straw, dear Henry, dear Henry, with a straw. \n");

      fflush(stdout);

      int stim1e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim1e);
      sleep(stim1e);

      printf("Then cut it, dear Henry, dear Henry, dear Henry, \n");
      printf("Then cut it, dear Henry, dear Henry, cut it. \n");

      fflush(stdout);
      int stim2e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim2e);
      sleep(stim2e);

      printf("With a knife, dear Henry, dear Henry, dear Henry, \n");
      printf("With a knife, dear Henry, dear Henry, with a knife. \n");
      fflush(stdout);

      int stim3e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim3e);
      sleep(stim3e);

      printf("Then sharpen it, dear Henry, dear Henry, dear Henry \n");
      printf("Then sharpen it, dear Henry, dear Henry, sharpen it. \n");

      fflush(stdout);
      int stim4e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim4e);
      sleep(stim4e);

      printf("On a stone, dear Henry, dear Henry, dear Henry, \n");
      printf("On a stone, dear Henry, dear Henry, a stone. \n");

      fflush(stdout);
      int stim5e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim5e);
      sleep(stim5e);

      printf("Well wet it, dear Henry, dear Henry, dear Henry, \n");
      printf("Well wet it, dear Henry, dear Henry, wet it. \n");

      fflush(stdout);
      int stim6e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim6e);
      sleep(stim6e);

      printf("try water, dear Henry, dear Henry, dear Henry, \n");
      printf("try water, dear Henry, dear Henry, water. \n");

      fflush(stdout);
      int stim7e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim7e);
      sleep(stim7e);

      printf("In a bucket, dear Henry, dear Henry, dear Henry, \n");
      printf("In a bucket, dear Henry, dear Henry, a bucket. \n");

      fflush(stdout);
      int stim8e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim8e);
      sleep(stim8e);

      printf("Use your head, then! dear Henry, dear Henry, dear Henry, \n");
      printf("Use your head, then! dear Henry, dear Henry, use your head! \n");

      fflush(stdout);

      operations[0].sem_op = 1;
      //signal
      retval = semop(id, operations, 1);

    }
  }else{
    //Process 2
    struct sembuf operations[1];
    int retval; /* Return value from semop() */
    /* Get the index for the semaphore with external name KEY. */
    id = semget(KEY, 1, 0666);
    if(id < 0){
      /* Semaphore does not exist. */

      fprintf(stderr, "Program sema cannot find semaphore, exiting.\n");
      exit(0);
    }
    operations[0].sem_num = 0;
    /* Which operation? Subtract 1 from semaphore value : */
    operations[0].sem_op = -1;
    /* Set the flag so we will wait : */   
    operations[0].sem_flg = 0;

    while(1){



      //wait
      operations[0].sem_op = -1;
      retval = semop(id, operations, 1);

      //critical section

      printf("There's a hole in the bucket, dear Liza, dear Liza, \n");
      printf("There's a hole in the bucket, dear Liza, a hole. \n");

      fflush(stdout);
      int stim9e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim9e);
      sleep(stim9e);

      printf("With what shall I mend it, dear Liza, dear Liza? \n");
      printf("With what shall I mend it, dear Liza, with what? \n");
      fflush(stdout);

      int stim0e=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stim0e);
      sleep(stim0e);

      printf("The straw is too long, dear Liza, dear Liza, \n");
      printf("The straw is too long, dear Liza, too long, \n");

      fflush(stdout);
      int stimae=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stimae);
      sleep(stimae);

      printf("The knife is too dull, dear Liza, dear Liza, \n");
      printf("The knife is too dull, dear Liza, too dull. \n");

      fflush(stdout);
      int stimse=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stimse);
      sleep(stimse);

      printf("On what shall I sharpen it, dear Liza, dear Liza? \n");
      printf("On what shall I sharpen it, dear Liza, on what? \n");

      fflush(stdout);
      int stimde=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stimde);
      sleep(stimde);

      printf("The stone is too dry, dear Liza, dear Liza, \n");
      printf("The stone is too dry, dear Liza, too dry. \n");

      fflush(stdout);
      int stimwe=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stimwe);
      sleep(stimwe);

      printf("With what shall I wet it, dear Liza, dear Liza? \n");
      printf("With what shall I wet it, dear Liza, with what? \n");

      fflush(stdout);
      int stimqe=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stimqe);
      sleep(stimqe);

      printf("In what shall I fetch it, dear Liza, dear Liza? \n");
      printf("In what shall I fetch it, dear Liza, in what? \n");

      fflush(stdout);
      int stimee=2+(rand()/(float)(RAND_MAX))*4;
      printf("Sleeping for %d secs\n",stimee);
      sleep(stimee);

      printf("There's a hole in my bucket, dear Liza, dear Liza \n ");
      printf("There's a hole in my bucket, dear Liza, a hole. \n ");
      fflush(stdout);

      //signal
      operations[0].sem_op = 1;
      retval = semop(id, operations, 1);

    }

  }

}

Semaphores do not enforce scheduling or ordering of critical sections, they're only for resource locking. What you want can be thought of as follows:

Given an ordered/sorted set of data and two 'workers' tasked with printing their contents to a stream:

  1. Each worker "waits" for their turn
  2. It acquires one datum (eg one line from the song)
  3. It prints the datum onto stdout
  4. It indicates to the other worker that it is done and it goes to sleep/wait

Semaphores are enough to solve this problem if you consider all steps 1-4 as a critical section, ie your "resources" are both a) the storage of your data and b) the output stream. To see an example of semaphore use for resource locking, see the dining philosophers problem .

The Henry thread needs to tell the Liza thread that he has finished delivering his line. A simple way to do this is through a variable.

const int HENRY_DONE = 0;
const inte LIZA_DONE = 1;
volatile int flag = HENRY_DONE;

Notice the volatile keyword? It tells the compiler not to store the variable in a register, it must be read from memory every time, because it may be modified externally (in this case by the other thread). I also added two constants for the two states, just to make the code easier to read. I you want, you can use an enum instead. It makes the code a bit prettier (and it also reduces the chance of someone writing the code line flag = -32 ).

  //critical section
  printf("Then mend it, dear Henry, dear Henry, dear Henry, \n");
  printf("Then mend it, dear Henry, dear Henry, mend it. \n");

  fflush(stdout);

  flag = LIZA_DONE;   // Hand over to Henry
  do {
    sleep(1); // If we don't sleep at all, there will be a busy wait. If you want to sleep for shorter than 1 s use usleep (located in `unistd.h`).
  } until (flag == HENRY_DONE);  // Wait for Henry to complete his next line.
  printf("With a straw, dear Henry, dear Henry, dear Henry, \n");
  printf("With a straw, dear Henry, dear Henry, with a straw. \n");

Do the same for Henrys part.

NOTE:

The above solution requires that the flag is placed in memory accessible to both LIZA and HENRY. If you use threads, it will work as is, but if you use fork then you need to place the flag in shared memory (example) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM