如何使用信号量强制执行进程之间的操作顺序?

时间:2013-03-25 08:03:30

标签: c process semaphore

我目前正在玩信号量并尝试理解它们。我试图按照一个教程,要求我编辑示例代码,让程序运行两个进程,轮流将歌词输出到歌曲('There's a hole in the bucket')。

我的问题是,当我在程序中添加更多行的歌曲时,进程不会交替,但只有两行它们才能正常工作。

一个过程处理Liza的部分,另一个处理Henry的部分。这是我的代码:

#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);

    }

  }

}

2 个答案:

答案 0 :(得分:0)

信号量不强制执行关键部分的调度或排序,它们仅用于资源锁定。您想要的可以被认为如下:

给定一个有序/有序的数据集和两个“工作人员”,负责将其内容打印到流中:

  1. 每个工人“等待”轮到他们
  2. 它获取一个数据(例如歌曲中的一行)
  3. 将数据打印到stdout
  4. 向其他工作人员表明已完成并进入睡眠/等待
  5. 如果您将所有步骤1-4视为关键部分,即您的“资源”既a)数据存储又b)输出流,则信号量足以解决此问题。要查看信号量用于资源锁定的示例,请参阅dining philosophers problem

答案 1 :(得分:0)

Henry线程需要告诉Liza线程他已经完成了他的线路。 一种简单的方法是通过变量。

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

请注意volatile关键字?它告诉编译器不要将变量存储在寄存器中,每次都必须从内存中读取它,因为它可以在外部修改(在这种情况下由另一个线程修改)。我还为这两种状态添加了两个常量,只是为了使代码更容易阅读。我想要你,你可以使用enum代替。它使代码更漂亮(并且还减少了某人编写代码行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");

为亨利部分做同样的事。

注意:

上述解决方案要求flag放在LIZA和HENRY都可以访问的内存中。如果您使用线程,它将按原样运行,但如果您使用fork,则需要将flag放在共享内存(example)中。