【问题标题】:global semaphores for synchronisation - counter用于同步的全局信号量 - 计数器
【发布时间】:2023-03-24 04:44:01
【问题描述】:

下面我在两个不同的进程中增加一个计数器 1000 并使用全局信号量进行同步。但是iCounter不给我2000?谁能给我解释一下为什么?

#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include <fcntl.h>

#define SEM_NAME "/sync"
#define NUMBER_OF_PROCESSES 2

int main ()
{
  int iCounter = 0;
  pid_t pid[NUMBER_OF_PROCESSES];
  sem_t *sSem = sem_open (SEM_NAME,O_CREAT | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO, 1);
  int i, iStatus;
  
  for (i = 0; i < NUMBER_OF_PROCESSES; i++)
    {
      pid[i] = fork ();
      if (pid[i] < 0)
    {
      printf ("Could not create process\n");
      exit (1);
    }
      else if (pid[i] == 0)
    {
      int i;
      for (i = 0; i < 1000; i++)
        {
          sem_init(sSem, 0, 1);
          sem_wait (sSem);
          iCounter++;
          sem_post (sSem);
        }
      exit (0);
    }
    }
  for (i = 0; i < NUMBER_OF_PROCESSES; i++)
    waitpid (pid[i], &iStatus, WUNTRACED);
  printf ("Value of iCounter = %d\n", iCounter);
  sem_close (sSem);
  sem_unlink (SEM_NAME);
}

【问题讨论】:

  • 发布的代码缺少语句:#include &lt;sys/types.h&gt;#include &lt;sys/wait.h&gt; 函数:waitpid() 你的编译器应该已经告诉你这个问题。
  • 发布的代码缺少语句:#include &lt;sys/types.h&gt;#include &lt;unistd.h&gt; 用于函数:fork()。你的编译器应该已经告诉你了。
  • 来自sem_init() 手册页:初始化已经初始化的信号量会导致未定义的行为。 实际上,它根本不应该使用,因为你是使用命名信号量。
  • 另外,iCounter 不会在您的进程之间共享。
  • 第二次运行发布的代码(当第一次执行从未完成并通过 退出时)导致“sem_open failed: File exists”建议删除| O_EXCL

标签: c concurrency semaphore


【解决方案1】:

你有两个大问题:

  1. 您没有正确使用命名信号量。 sem_init() 仅用于匿名信号量,并且每个信号量仅使用一次,您在已经初始化的信号量上多次调用它。这是未定义的行为。

  2. 您的 iCounter 变量未在您创建的进程之间共享。

您的程序的以下修改使用 POSIX 共享内存分配它(并进行了一些常规清理):

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

#define SEM_NAME "/sync"
#define SHM_NAME "/counter"
#define NUMBER_OF_PROCESSES 2

int main() {
  int *iCounter = MAP_FAILED;
  pid_t pid[NUMBER_OF_PROCESSES];
  int iStatus;
  int ret = 0;
  int memfd = -1;
  
  sem_t *sSem = sem_open(SEM_NAME, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 1);
  if (sSem == SEM_FAILED) {
    perror("sem_open");
    ret = 1;
    goto cleanup;
  }

  memfd = shm_open(SHM_NAME, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
  if (memfd < 0) {
    perror("shm_open");
    ret = 1;    
    goto cleanup;
  }

  if (ftruncate(memfd, sizeof *iCounter) < 0) {
    perror("ftruncate");
    ret = 1;
    goto cleanup;
  }
  iCounter = mmap(NULL, sizeof *iCounter, PROT_READ | PROT_WRITE, MAP_SHARED,
                  memfd, 0);
  if (iCounter == MAP_FAILED) {
    perror("mmap");
    ret = 1;
    goto cleanup;
  }
  close(memfd);
  *iCounter = 0;

  for (int i = 0; i < NUMBER_OF_PROCESSES; i++) {
    pid[i] = fork();
    if (pid[i] < 0) {
      fprintf(stderr, "Could not create process: %s\n", strerror(errno));
      ret = 1;
      goto cleanup;
    } else if (pid[i] == 0) {
      for (i = 0; i < 1000; i++) {
        sem_wait(sSem);
        (*iCounter)++;
        sem_post(sSem);
      }
      sem_close(sSem);
      return 0;
    }
  }
  for (int i = 0; i < NUMBER_OF_PROCESSES; i++)
    waitpid(pid[i], &iStatus, WUNTRACED);

  printf("Value of iCounter = %d\n", *iCounter);

 cleanup:
  if (sSem != SEM_FAILED) {
    sem_close(sSem);
    sem_unlink(SEM_NAME);
  }
  if (memfd >= 0)
    shm_unlink(SHM_NAME);

  return ret;
}

例子:

$ gcc -g -O -Wall -Wextra example.c -lrt -lpthread
$ ./a.out
Value of iCounter = 2000

【讨论】:

  • 由于子进程都是由同一个父进程创建的,因此您并不真正需要 POSIX 共享内存,因为匿名mmap() 也可以正常工作(并且在共享内存中分配匿名信号量而不是使用sem_open()),但由于您使用的是 POSIX 信号量,因此不妨也演示一下它的兄弟。
  • 错误处理有一些逻辑问题:例如关于:sem_t *sSem = sem_open(SEM_NAME, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 1); if (sSem == SEM_FAILED) { perror("sem_open"); ret = 1; goto cleanup; ....cleanup: sem_unlink(SEM_NAME); shm_unlink(SHM_NAME);这将是一个问题,因为它试图取消链接从未分配过的共享内存。
  • @user3629249 取消链接不存在的对象是可以安全忽略的错误。
猜你喜欢
  • 1970-01-01
  • 2016-08-04
  • 2015-07-03
  • 2018-09-25
  • 1970-01-01
  • 1970-01-01
  • 2013-05-30
  • 1970-01-01
  • 2011-04-25
相关资源
最近更新 更多