分叉的子进程是不是使用相同的信号量?

Posted

技术标签:

【中文标题】分叉的子进程是不是使用相同的信号量?【英文标题】:Do forked child processes use the same semaphore?分叉的子进程是否使用相同的信号量? 【发布时间】:2011-10-14 10:44:30 【问题描述】:

假设我创建了一个信号量。如果我 fork 一堆子进程,它们还会使用相同的信号量吗?

另外,假设我创建了一个内部带有信号量并分叉的结构。所有子进程是否仍然使用相同的信号量?如果不是,将结构+信号量存储在共享内存中是否允许子进程使用相同的信号量?

我真的很困惑我的分叉子进程如何使用相同的信号量。

【问题讨论】:

【参考方案1】:

假设我创建了一个信号量。如果我 fork 一堆子进程,它们还会使用相同的信号量吗?

如果您使用的是 SysV IPC 信号量 (semctl),那么可以。如果您使用的是 POSIX 信号量 (sem_init),那么可以,但 only if you pass a true value for the pshared argument 在创建时将其放置在共享内存中。

另外,假设我创建了一个内部带有信号量并分叉的结构。所有子进程是否仍然使用相同的信号量?如果不是,将结构+信号量存储在共享内存中是否允许子进程使用相同的信号量?

“内部信号量”是什么意思? SysV IPC 信号量的引用将被共享,因为信号量不属于任何进程。如果您使用 POSIX 信号量,或者从 pthreads 互斥体和 condvars 构造某些东西,则需要使用共享内存和 pshared 属性(pthreads 也有一个用于 condvars 和 mutexes 的 pshared 属性)

请注意,出于这些目的,使用MAP_SHARED 标志创建的匿名 mmap 算作(匿名)共享内存,因此没有必要实际创建命名共享内存段。 fork 后不会共享普通堆内存

【讨论】:

为了澄清,我创建了一个结构并在结构内放置了我的 POSIX 信号量。然后我把这个结构放到共享内存中。这样做时,我是否可以假设我的所有子进程都在正确访问相同的信号量? 我用 sem_init(&p.mysem, 1, 1) 创建了我的信号量。其中 p 是一个结构,而 mysem 是结构内部的一个 sem_t。这看起来正确吗? @canistr,该结构必须放置在共享内存中(使用 mmap 的 MAP_SHARED 标志映射的内存),并且必须使用 sem_init 创建信号量,并为 pshared 标志传递 1。如果满足这些条件,子进程将正确共享信号量。 那么如何将 mmap 与共享内存一起使用? @canistr,这有点超出了这个问题的范围。如果您搜索一下,有很多关于使用 mmap 共享内存的资源,如果您仍然遇到问题,请随时就该主题提出另一个问题【参考方案2】:

假设我创建了一个信号量。如果我 fork 一堆子进程, 他们还会使用相同的信号量吗?

这取决于您如何创建信号量,要使用 IPC 信号量来执行此操作,请参阅 semaphore.c: Illustration of simple semaphore passing 以获取示例。

另外,假设我创建了一个内部带有信号量并分叉的结构。做 所有子进程仍然使用相同的信号量吗?如果没有,会 将结构+信号量存储在共享内存中允许孩子 进程使用相同的信号量?

为此,您的信号量需要存储在父进程和子进程之间共享的区域(如共享内存)中,而不仅仅是在堆栈或堆上创建,因为它会在进程分叉时被复制。

我真的很困惑我的分叉子进程如何使用 相同的信号量。

信号量可以跨线程或进程共享。跨进程共享是在操作系统层面实现的。两个或多个不同的进程可以共享相同的信号量,即使这些进程不是通过派生单个父进程创建的。

查看此示例以在父进程及其子进程之间共享一个未命名的 UNIX 信号量(要使用 gcc 编译,您需要 -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)

  /* place semaphore in shared memory */
  sem_t *sema = mmap(NULL, sizeof(*sema), 
      PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,
      -1, 0);
  if (sema == MAP_FAILED) 
    perror("mmap");
    exit(EXIT_FAILURE);
  

  /* 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) 
    perror("fork");
    exit(EXIT_FAILURE);
  
  if (pid == 0)  
    /* child process*/
    for (int i = 0; i < nloop; i++) 
      printf("child unlocks semaphore: %d\n", i);
      if (sem_post(sema) < 0) 
          perror("sem_post");
      
      sleep(1);
    
    if (munmap(sema, sizeof(sema)) < 0) 
      perror("munmap");
      exit(EXIT_FAILURE);
    
      exit(EXIT_SUCCESS);
  
  if (pid > 0) 
    /* back to parent process */
    for (int i = 0; i < nloop; i++) 
      printf("parent starts waiting: %d\n", i);
      if (sem_wait(sema) < 0) 
        perror("sem_wait");
      
      printf("parent finished waiting: %d\n", i);
    
    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);
  

输出将是:

parent starts waiting: 0
child unlocks semaphore: 0
parent finished waiting: 0
parent starts waiting: 1
child unlocks semaphore: 1
parent finished waiting: 1
...

您可能还想阅读Semaphores in Linux,但请注意,由于作者忘记在mmap 中使用MAP_ANONYMOUS 标志,因此跨fork 的UNIX 信号量示例不起作用。

【讨论】:

所以如果我想让分叉的孩子共享一个信号量,他们需要是 IPC 信号量。我可以将这些信号量存储在一个结构中并将其传递给不同的函数吗?我对您的第二点感到困惑,因为我想使用共享内存在子进程之间传递信息并用这些信号量保护它们。 如果我想专门使用未命名的信号量,这与 IPC 信号量有很大不同吗? @canistr:您确实可以使用共享内存在进程之间共享信息,只是不要将信号量放入该内存中。看看我提供的第一个例子 - 它确实fork () 并且孩子和父母都在没有共享内存的情况下使用相同的信号量。在此示例之上添加您想要共享的信息的共享内存,您会没事的。 在“Linux 中的信号量”中链接到的 fork 示例中使用未命名的信号量是错误的。您需要添加匿名标志,或使用命名信号量。 @Étienne:很酷,您介意编辑答案并使其成为社区维基吗?欢迎使用工作代码示例:)【参考方案3】:

试试这个

孩子和父母会交替增加共享变量

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

struct test 
        sem_t mutex1;
        sem_t mutex2;
        int temp;
test1;

int main(int argc, char **argv)

  int fd, i,count=0,nloop=10,zero=0,*ptr;
  struct test *testptr;
  //open a file and map it into memory
        sem_t mutex;
  fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);
  write(fd,&zero,sizeof(int));
  ptr = mmap(NULL, sizeof(struct test),PROT_READ |PROT_WRITE,MAP_SHARED,fd,0);
  close(fd);
  memcpy(ptr, &test1, sizeof(test1));
  testptr = (struct test *)ptr;
  // testptr = (struct test *)&test1;
  /* create, initialize semaphore */
  if( sem_init(&(testptr->mutex1),1,1) < 0)
    
      perror("semaphore initilization");
      exit(0);
    
  /* create, initialize semaphore */
  if( sem_init(&(testptr->mutex2),1,0) < 0)
    
      perror("semaphore initilization");
      exit(0);
    
  if (fork() == 0)  /* child process*/
    for (i = 0; i < nloop; i++) 
      sem_wait(&(testptr->mutex2));
      printf("child: %d\n", testptr->temp++);
      sem_post(&(testptr->mutex1));
    
    exit(0);
 /* back to parent process */
  for (i = 0; i < nloop; i++) 
    sem_wait(&testptr->mutex1);
    printf("parent: %d\n", testptr->temp++);
    sem_post(&(testptr->mutex2));
  
  exit(0);

【讨论】:

-1:您从linuxdevcenter.com/pub/a/linux/2007/05/24/… 复制了此代码,但没有提及它不是您的。加上这个代码不起作用!解释见blog.superpat.com/2010/07/14/…。

以上是关于分叉的子进程是不是使用相同的信号量?的主要内容,如果未能解决你的问题,请参考以下文章

ruby 等待所有分叉的子进程完成

Node.js 将响应对象句柄的句柄传递给子进程

C++ fork 进程但不是子进程

fork 和 signal:如何将信号从父进程发送到特定的子进程

如何创建一个不是其创建过程的子进程?

在父进程收到终止信号并退出后,让 system() 生成的子进程继续运行