c - 如何在 C 中的 fork 进程上使用 POSIX 信号量?

标签 c linux fork shared-memory semaphore

我想 fork 多个进程,然后对它们使用信号量。这是我尝试过的:

sem_init(&sem, 1, 1);   /* semaphore*, pshared, value */
.
.
.
if(pid != 0){ /* parent process */
    wait(NULL); /* wait all child processes */

    printf("\nParent: All children have exited.\n");
    .
    .
    /* cleanup semaphores */
    sem_destroy(&sem);      
    exit(0);
}
else{ /* child process */
    sem_wait(&sem);     /* P operation */
    printf("  Child(%d) is in critical section.\n",i);
    sleep(1);
    *p += i%3;  /* increment *p by 0, 1 or 2 based on i */
    printf("  Child(%d) new value of *p=%d.\n",i,*p);
    sem_post(&sem);     /* V operation */
    exit(0);
}

输出是:

child(0) forked
child(1) forked
  Child(0) is in critical section.
  Child(1) is in critical section.
child(2) forked
  Child(2) is in critical section.
child(3) forked
  Child(3) is in critical section.
child(4) forked
  Child(4) is in critical section.
  Child(0) new value of *p=0.
  Child(1) new value of *p=1.
  Child(2) new value of *p=3.
  Child(3) new value of *p=3.

  Child(4) new value of *p=4.
Parent: All children have exited.

这显然意味着信号量没有按预期工作。你能解释一下我应该如何在 fork 进程上使用信号量吗?

最佳答案

您遇到的问题是对sem_init()函数的误解。当您阅读 manual page 你会看到这个:

The pshared argument indicates whether this semaphore is to be shared between the threads of a process, or between processes.

如果你读到这里,你会认为 pshared 的非零值会使信号量成为进程间信号量。然而,这是错误的。 您应该继续阅读,您将了解到必须在共享内存区域中定位信号量。为此,可以使用几个函数作为 你可以在下面看到:

If pshared is nonzero, then the semaphore is shared between processes, and should be located in a region of shared memory (see shm_open(3), mmap(2), and shmget(2)). (Since a child created by fork(2) inherits its parent's memory mappings, it can also access the semaphore.) Any process that can access the shared memory region can operate on the semaphore using sem_post(3), sem_wait(3), etc.

我发现这种方法比其他方法更复杂,因此我想鼓励人们使用 sem_open() 而不是 sem_init()

下面你可以看到一个完整的程序说明如下:

  • 如何在fork之间分配共享内存和使用共享变量 流程。
  • 如何在共享内存区域中初始化信号量并使用 由多个进程。
  • 如何 fork 多个进程并让父进程等到所有进程 它的 child 退出。
#include <stdio.h>          /* printf()                 */
#include <stdlib.h>         /* exit(), malloc(), free() */
#include <sys/types.h>      /* key_t, sem_t, pid_t      */
#include <sys/shm.h>        /* shmat(), IPC_RMID        */
#include <errno.h>          /* errno, ECHILD            */
#include <semaphore.h>      /* sem_open(), sem_destroy(), sem_wait().. */
#include <fcntl.h>          /* O_CREAT, O_EXEC          */


int main (int argc, char **argv){
    int i;                        /*      loop variables          */
    key_t shmkey;                 /*      shared memory key       */
    int shmid;                    /*      shared memory id        */
    sem_t *sem;                   /*      synch semaphore         *//*shared */
    pid_t pid;                    /*      fork pid                */
    int *p;                       /*      shared variable         *//*shared */
    unsigned int n;               /*      fork count              */
    unsigned int value;           /*      semaphore value         */

    /* initialize a shared variable in shared memory */
    shmkey = ftok ("/dev/null", 5);       /* valid directory name and a number */
    printf ("shmkey for p = %d\n", shmkey);
    shmid = shmget (shmkey, sizeof (int), 0644 | IPC_CREAT);
    if (shmid < 0){                           /* shared memory error check */
        perror ("shmget\n");
        exit (1);
    }

    p = (int *) shmat (shmid, NULL, 0);   /* attach p to shared memory */
    *p = 0;
    printf ("p=%d is allocated in shared memory.\n\n", *p);

    /********************************************************/

    printf ("How many children do you want to fork?\n");
    printf ("Fork count: ");
    scanf ("%u", &n);

    printf ("What do you want the semaphore value to be?\n");
    printf ("Semaphore value: ");
    scanf ("%u", &value);

    /* initialize semaphores for shared processes */
    sem = sem_open ("pSem", O_CREAT | O_EXCL, 0644, value); 
    /* name of semaphore is "pSem", semaphore is reached using this name */

    printf ("semaphores initialized.\n\n");


    /* fork child processes */
    for (i = 0; i < n; i++){
        pid = fork ();
        if (pid < 0) {
        /* check for error      */
            sem_unlink ("pSem");   
            sem_close(sem);  
            /* unlink prevents the semaphore existing forever */
            /* if a crash occurs during the execution         */
            printf ("Fork error.\n");
        }
        else if (pid == 0)
            break;                  /* child processes */
    }


    /******************************************************/
    /******************   PARENT PROCESS   ****************/
    /******************************************************/
    if (pid != 0){
        /* wait for all children to exit */
        while (pid = waitpid (-1, NULL, 0)){
            if (errno == ECHILD)
                break;
        }

        printf ("\nParent: All children have exited.\n");

        /* shared memory detach */
        shmdt (p);
        shmctl (shmid, IPC_RMID, 0);

        /* cleanup semaphores */
        sem_unlink ("pSem");   
        sem_close(sem);  
        /* unlink prevents the semaphore existing forever */
        /* if a crash occurs during the execution         */
        exit (0);
    }

    /******************************************************/
    /******************   CHILD PROCESS   *****************/
    /******************************************************/
    else{
        sem_wait (sem);           /* P operation */
        printf ("  Child(%d) is in critical section.\n", i);
        sleep (1);
        *p += i % 3;              /* increment *p by 0, 1 or 2 based on i */
        printf ("  Child(%d) new value of *p=%d.\n", i, *p);
        sem_post (sem);           /* V operation */
        exit (0);
    }
}

输出

./a.out 
shmkey for p = 84214791
p=0 is allocated in shared memory.

How many children do you want to fork?
Fork count: 6 
What do you want the semaphore value to be?
Semaphore value: 2
semaphores initialized.

  Child(0) is in critical section.
  Child(1) is in critical section.
  Child(0) new value of *p=0.
  Child(1) new value of *p=1.
  Child(2) is in critical section.
  Child(3) is in critical section.
  Child(2) new value of *p=3.
  Child(3) new value of *p=3.
  Child(4) is in critical section.
  Child(5) is in critical section.
  Child(4) new value of *p=4.
  Child(5) new value of *p=6.

Parent: All children have exited.

检查 shmkey 还不错,因为当 ftok() 失败时,它会返回 -1。但是,如果您有多个共享变量并且 如果 ftok() 函数多次失败,具有值为 -1shmkey 的共享变量将驻留在同一个 共享内存的区域导致一个影响另一个的变化。因此程序执行会变得困惑。为避免这种情况,最好检查 ftok() 是否返回 -1(最好 checkin 源代码而不是像我那样打印到屏幕,尽管我想向您展示键值以防发生冲突)。

注意信号量是如何声明和初始化的。这与您在问题中所做的不同(sem_t sem vs sem_t* sem)。此外,您应该按照本例中出现的方式使用它们。您不能定义 sem_t* 并在 sem_init() 中使用它。

关于c - 如何在 C 中的 fork 进程上使用 POSIX 信号量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16400820/

相关文章:

C fgets问题

linux - 创建可引导的 .img 文件

linux - Motion 3.2.12是否支持RTSP MJPEG

linux - 有什么方法可以在linux中保留但不提交内存?

C并发进程

perl 后台进程

c - 为什么 pow(n,2) 在 n=5 时使用我的编译器和操作系统返回 24?

c - 为什么第二个代码出现段错误?

C 枚举 : unknown type name

multithreading - 控制 Fork super 队列的长度