1

所以我在 C 中的进程同步程序遇到了这个问题。

我应该编写一个代码,使用fork(),将产生如下内容:

PARENT
PARENT
CHILD
PARENT
CHILD
PARENT

使用我在这里找到的代码,我能够让它工作,但由于某些原因,屏幕上的第一个结果是混乱的,而其他所有的工作都很好。

要编译,请键入:gcc test.c display.c -o test -pthread

无论如何,这是我正在测试的代码(我重复一遍:这不是我的代码):

#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

int main(void)
{
  int i;
  /* place semaphore in shared memory */
  sem_t *sema = mmap(NULL, sizeof(sema), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);
  /* create/initialize semaphore */
  if ( sem_init(sema, 1, 0) < 0)
  {
    perror("sem_init");
    exit(EXIT_FAILURE);
  }
  int nloop=10;
  int pid = fork();
  if (pid == 0)
  { 
    for (i = 0; i < nloop; i++)
    {
      // child unlocks semaphore
      display("CHILD\n");
      if (sem_post(sema) < 0)
          perror("sem_post");
      sleep(1);
    }
    if (munmap(sema, sizeof(sema)) < 0)
    {
      perror("munmap");
      exit(EXIT_FAILURE);
    }
      exit(EXIT_SUCCESS);
  }
  else
  {
    for (i = 0; i < nloop; i++)
    { // parent starts waiting
      display("PARENT\n");
      if (sem_wait(sema) < 0) 
        perror("sem_wait");
    // parent finished waiting
    }
    if (sem_destroy(sema) < 0)
    {
      perror("sem_destroy failed");
      exit(EXIT_FAILURE);
    }
    if (munmap(sema, sizeof(sema)) < 0)
    {
      perror("munmap failed");
      exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
  }
}

这是输出:

PACREHNT
ILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD

为什么一开始会发生这种情况?

4

1 回答 1

2

你不能编写一个程序,用一个信号量在父进程和子进程之间交替(不诉诸某种形式的忙等待标志或其他东西),因为两个进程都会竞相获取信号量;无法预测哪个进程将首先获取它。您的代码(第一次迭代除外)似乎按预期工作,因为孩子睡了很长时间,但从技术上讲,它仍然是一个竞争条件,不能保证父母有机会在孩子醒来(尽管可能性很小)。

因此,您需要 2 个信号量:一个由孩子用来通知父母轮到他了,另一个由父母用来通知孩子。要选择谁先开始,请将相应的信号量初始化为 1(另一个为 0)。

另外,这是错误的:

sem_t *sema = mmap(NULL, sizeof(sema), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);

第二个参数应该是sizeof(*sema)因为您要为信号量对象分配内存,而不是为指针分配内存。

你从来没有#include "display.h",你可能应该。

错误处理可以改进,但对于这个玩具程序,我认为这没什么大不了的。

这是一个使用 2-semaphore 方法的工作版本(这会将父信号量初始化为 1,因此父信号量将首先启动):

#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

int main(void)
{
    int i;
    /* place semaphore in shared memory */
    sem_t *child_sem = mmap(NULL, sizeof(*child_sem), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);
    sem_t *parent_sem = mmap(NULL, sizeof(*parent_sem), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);

    /* create/initialize semaphore */
    if ( sem_init(child_sem, 1, 0) < 0)
    {
        perror("sem_init");
        exit(EXIT_FAILURE);
    }

    if (sem_init(parent_sem, 1, 1) < 0) {
        perror("sem_init");
        exit(EXIT_FAILURE);
    }

    int nloop=10;
    int pid = fork();
    if (pid == 0)
    {
        for (i = 0; i < nloop; i++)
        {
            if (sem_wait(child_sem) < 0)
                perror("sem_wait");
            display("CHILD\n");
            if (sem_post(parent_sem) < 0)
                perror("sem_post");
            sleep(1);
        }
    }
    else
    {
        for (i = 0; i < nloop; i++)
        { // parent starts waiting
            if (sem_wait(parent_sem) < 0) 
                perror("sem_wait");
            display("PARENT\n");
            if (sem_post(child_sem) < 0)
                perror("sem_post");
        }
    }
}

为了简洁起见,我删除了munmap(2)and ,因为它们是不必要的,因为无论如何都会退出进程。sem_destroy(3)

请注意,两个进程都遵循相同的模式:等待它们的信号量,做工作,通知另一个进程的信号量。这是进行一些重构并将其全部移至接收要显示的字符串、要等待的信号量以及随后要通知的信号量的函数的好机会。

您还应该习惯使用-Wall.

于 2015-12-12T13:16:52.630 回答