【问题标题】:Why are my semaphores allowing out-of-order events within forked processes?为什么我的信号量允许分叉进程中发生乱序事件?
【发布时间】:2017-04-03 05:31:40
【问题描述】:

我正在尝试制作一个从 0 计数到 C 命令行中输入的任何数字的程序。在这个程序中,必须有两个 fork() 调用,总共有 3 个进程。然后我必须使用至少 1 个信号量来确保进程按数字顺序运行,每个进程负责不同的 n % 3。

我遇到的问题是,尽管我使用了信号量,但程序似乎经常出现故障。我目前正在使用一个门式系统,其中每个进程都会强制它自己指定的信号量等待,一旦完成,sem_post 应该运行下一个进程的信号量。我知道这不是最漂亮或最合乎逻辑的有效方式,但在我遇到同样问题的前两次不同尝试之后,我很确定它会奏效。

如果有人能给我任何关于我哪里出错的建议,我将非常感激。

我的代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <semaphore.h>
    #include <fcntl.h>

    #define SEM_NAME1 "/sem1.mutex"
    #define SEM_NAME2 "/sem2.mutex"
    #define SEM_NAME3 "/sem3.mutex"

    int main(int argc, char *argv[]) {

        if(argc <= 1){
            printf("No arguments were provided so there is no number to count to");
            return 1;
        }

        // Create the 3 semaphores needed
        sem_t *sem1;
        sem_t *sem2;
        sem_t *sem3;

        //initialize to 0
        sem1 = sem_open(SEM_NAME1, O_CREAT, O_RDWR, 0);
        if (sem1==SEM_FAILED) {
            printf("%s sem_open failed!", SEM_NAME1);
            return (-1);
        }
        //initialize to 1
        sem2 = sem_open(SEM_NAME2, O_CREAT, O_RDWR, 1);
        if (sem2==SEM_FAILED) {
            printf("%s sem_open failed!", SEM_NAME2);
            return (-1);
        }
        //initialize to 1
        sem3 = sem_open(SEM_NAME3, O_CREAT, O_RDWR, 1);
        if (sem3==SEM_FAILED) {
            printf("%s sem_open failed!", SEM_NAME3);
            return (-1);
        }

        pid_t pid;
        pid_t pid2;
        pid = fork();

        if(pid == 0){
            pid2 = fork();
        }
        // Shared fork variables
        int counter = 0;
        int ranOnce = 0;
        int max_num = atoi(argv[1]);

        while(counter <= max_num){
            if(pid > 0){
                printf("%d",getpid());
                if(ranOnce == 0){
                    counter += 1;
                    ranOnce = 1;
                }
                sem_wait(sem2);
                printf(" %d \n", counter);
                counter += 3;
                sem_post(sem3);
            }
            else if(pid2 == 0){
                printf("%d",getpid());
                if(ranOnce == 0){
                    counter += 0;
                    ranOnce = 1;
                }
                sem_wait(sem1);
                printf(" %d \n", counter);
                counter += 3;
                sem_post(sem2);
            }
            else{
                printf("%d",getpid());
                if(ranOnce == 0){
                    counter += 2;
                    ranOnce = 1;
                }
                sem_wait(sem3);
                printf(" %d \n", counter);
                counter += 3;
                sem_post(sem1);
            }
        }
        //sem_unlink(SEM_NAME1);
        //sem_unlink(SEM_NAME2);
        //sem_unlink(SEM_NAME3);
        return 0;
    }

【问题讨论】:

    标签: c semaphore


    【解决方案1】:

    信号量初始值必须是相反的

    sem1 = sem_open(SEM_NAME1, O_CREAT, O_RDWR, 1);
    sem2 = sem_open(SEM_NAME2, O_CREAT, O_RDWR, 0);
    sem3 = sem_open(SEM_NAME3, O_CREAT, O_RDWR, 0);
    

    您当前同时激活 sem2 和 sem3 进程,因此它们同时工作,但您需要同步它们以移除乱序事件。

    【讨论】:

      【解决方案2】:

      抱歉,您的代码无法按设计运行。

      fork 调用之后,每个进程都有自己的“共享”变量的私有副本。与线程不同,您必须使用通过 SysV 共享内存原语设置的共享内存区域。

      因此,每个进程都在增加自己的副本,不是counter 的共享副本。

      此外,所有对共享变量的访问[包括while 子句]必须封装在锁中。


      我不确定您是在第三句中暗示我想使用共享变量还是应该使用单独的副本。

      我正在查看您的评论:// Shared fork variables,所以我推断您想要一个通用计数器。 IMO,您确实想要一个作为交叉检查

      我知道每个人都只是访问他们自己的计数器变量的个人副本,这就是信号量门的目的。当线程被允许再次运行时,意味着其他两个进程都完成了打印,因此序列中的下一个字符将比计数器的当前值提前 3。

      我已重新编码您的应用程序以简化它,以便我能理解它。

      我没有看到一个干净的循环序列。如果你现在,一切都很好,我可能已经引入了一个错误。我使用shmget 等添加了一个共享内存全局计数器。人。它应该增加一,但它偶尔会向后滑动 [警告:这里太晚了,我累了,所以这可能是问题的一部分:-)]

      无论如何,这就是我想出的:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <errno.h>
      #include <time.h>
      #include <sys/types.h>
      #include <sys/wait.h>
      #include <unistd.h>
      #include <semaphore.h>
      #include <fcntl.h>
      #include <sys/ipc.h>
      #include <sys/shm.h>
      
      #define SEM_NAME1 "/sem1.mutex"
      #define SEM_NAME2 "/sem2.mutex"
      #define SEM_NAME3 "/sem3.mutex"
      
      typedef struct tsk {
          int tsk_idx;
          sem_t *tsk_sem;
          pid_t tsk_pid;
          char tsk_name[100];
      } tsk_t;
      
      #define NTASK       3
      
      tsk_t tsklist[NTASK];
      
      // Shared fork variables
      volatile int counter = 0;
      volatile int ranOnce = 0;
      int max_num;
      volatile int *globptr;
      
      void
      dochild(tsk_t *tsk)
      {
          int stopflg;
          int tidx;
          tsk_t *tsk2;
      
          while (1) {
              sem_wait(tsk->tsk_sem);
              stopflg = (counter > max_num);
      
              if (! stopflg) {
                  printf("Tidx:%d Pid:%d Seq:%d",tsk->tsk_idx,tsk->tsk_pid,*globptr);
                  *globptr += 1;
                  if (ranOnce == 0) {
                      counter += 1;
                      ranOnce = 1;
                  }
                  printf(" Counter:%d\n", counter);
                  fflush(stdout);
                  counter += 3;
              }
      
              tidx = tsk->tsk_idx + 1;
              tidx %= NTASK;
              tsk2 = &tsklist[tidx];
      
              sem_post(tsk2->tsk_sem);
      
              if (stopflg)
                  break;
          }
      
          exit(0);
      }
      
      int
      main(int argc, char *argv[])
      {
          tsk_t *tsk;
          int tidx;
          void *sptr;
      
          if (argc <= 1) {
              printf("No arguments were provided so there is no number to count to\n");
              //return 1;
          }
          else
              max_num = atoi(argv[1]);
      
          int shmfd = shmget(IPC_PRIVATE,sizeof(int),0600);
          sptr = shmat(shmfd,NULL,0);
          globptr = sptr;
          *globptr = 0;
      
          // Create the 3 semaphores needed
          for (tidx = 0;  tidx < NTASK;  ++tidx) {
              tsk = &tsklist[tidx];
              tsk->tsk_idx = tidx;
      
              sprintf(tsk->tsk_name,"/sem%d.mutex",tidx);
      
              if (max_num == 0) {
                  sem_unlink(tsk->tsk_name);
                  continue;
              }
      
              tsk->tsk_sem = sem_open(tsk->tsk_name, O_CREAT, O_RDWR,
                  0644,(tidx == 0) ? 1 : 0);
      
              if (tsk->tsk_sem == SEM_FAILED) {
                  printf("%s sem_open failed! -- %s\n",tsk->tsk_name,strerror(errno));
                  return (-1);
              }
          }
      
          if (max_num == 0)
              return 0;
      
          for (tidx = 0;  tidx < NTASK;  ++tidx) {
              tsk = &tsklist[tidx];
              tsk->tsk_pid = fork();
              if (tsk->tsk_pid != 0)
                  continue;
      
              tsk->tsk_pid = getpid();
              dochild(tsk);
          }
      
          for (tidx = 0;  tidx < NTASK;  ++tidx) {
              tsk = &tsklist[tidx];
              waitpid(tsk->tsk_pid,NULL,0);
          }
      
      #if 1
          for (tidx = 0;  tidx < NTASK;  ++tidx) {
              tsk = &tsklist[tidx];
              sem_unlink(tsk->tsk_name);
          }
      #endif
      
          shmdt(sptr);
      
          return 0;
      }
      

      这是运行的输出:

      Tidx:0 Pid:18563 Seq:0 Counter:1
      Tidx:0 Pid:18563 Seq:1 Counter:4
      Tidx:0 Pid:18563 Seq:2 Counter:7
      Tidx:0 Pid:18563 Seq:3 Counter:10
      Tidx:1 Pid:18564 Seq:4 Counter:1
      Tidx:1 Pid:18564 Seq:5 Counter:4
      Tidx:1 Pid:18564 Seq:6 Counter:7
      Tidx:1 Pid:18564 Seq:7 Counter:10
      Tidx:2 Pid:18565 Seq:4 Counter:1
      Tidx:2 Pid:18565 Seq:9 Counter:4
      Tidx:2 Pid:18565 Seq:10 Counter:7
      Tidx:2 Pid:18565 Seq:11 Counter:10
      

      【讨论】:

      • 我不确定您是在第三句中暗示我想使用共享变量还是应该使用单独的副本。我知道每个人都只访问他们自己的计数器变量的个人副本,这就是信号量门的目的。当线程被允许再次运行时,意味着其他两个进程都已完成打印,因此序列中的下一个字符将比当前计数器的值提前 3。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-05
      • 2019-10-16
      • 1970-01-01
      • 1970-01-01
      • 2012-09-18
      • 1970-01-01
      相关资源
      最近更新 更多