【问题标题】:How can I enforce order of operations between processes using semaphores?如何使用信号量强制执行进程之间的操作顺序?
【发布时间】:2023-04-01 03:56:01
【问题描述】:

我目前正在使用信号量并试图理解它们。我正在尝试遵循一个教程,该教程要求我编辑示例代码以使程序运行两个进程,这些进程轮流将歌词输出到歌曲('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);

    }

  }

}

【问题讨论】:

  • TL;博士!请阅读sscce.org
  • 你能创建一个问题的最小例子吗?
  • 对不起,这将有助于输出应该去:桶里有一个洞,亲爱的丽莎,亲爱的丽莎,桶里有一个洞,亲爱的丽莎,一个洞。紧随其后的是,然后修补它,亲爱的亨利,亲爱的亨利,亲爱的亨利,然后修补它,亲爱的亨利,亲爱的亨利,修补它。并继续在两个进程之间切换
  • 如果要保证进程轮流输出,那么每次切换都需要进行切换。我看到线路之间没有这样的切换,只是一个随机长度的睡眠。
  • @KlasLindbäck 我明白你的意思,你能指点我如何实现移交的教程吗?

标签: c process semaphore


【解决方案1】:

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) 中。

【讨论】:

  • 我已经尝试实现您的示例,但由于某种原因,Liza 和 Henry 的前两行都是正确的,但之后,换行了。类似于:(Henry、Liza、Liza、Henry、Liza、Henry 等)可能是什么原因?
  • 因为我的回答是错误的——fork 会给 LIZA 和 HENRY 自己的所有变量副本,他们将无法检测到对方所做的更改。请参阅添加的注释。
【解决方案2】:

信号量不强制关键部分的调度或排序,它们仅用于资源锁定。你想要的可以这样想:

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

  1. 每个工人“等待”轮到他们
  2. 它获取一个数据(例如歌曲中的一行)
  3. 它将数据打印到标准输出上
  4. 它向其他工作人员指示它已完成并进入睡眠/等待状态

如果您将所有步骤 1-4 视为关键部分,则信号量足以解决此问题,即您的“资源”既是 a) 数据的存储,又是 b) 输出流。要查看信号量用于资源锁定的示例,请参阅dining philosophers problem

【讨论】:

  • 嗨,我看过哲学家就餐问题,它的运行似乎与我的代码相似。无论如何,您是否可以指出我的代码中问题所在的方向@klas lindback 说我需要进行移交,但我不知道如何做到这一点
猜你喜欢
  • 2012-04-28
  • 2013-05-21
  • 1970-01-01
  • 2021-04-25
  • 2010-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-01
相关资源
最近更新 更多