如何使用POSIX信号上用C派生进程?(How to use POSIX semaphores on

2019-09-02 02:35发布

我想叉多个进程,然后对他们使用一个信号量。 这里是我的尝试:

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.

这显然意味着,因为它本来是要信号灯没有工作。 你能解释一下我应该怎么上派生的进程使用的信号灯?

Answer 1:

你所面对的问题是误解sem_init()函数。 当你阅读的手册页 ,你会看到这一点:

pshared的参数指示此信号量是否将被共享的进程的线程之间,或进程。

如果你读完了这一点,你会觉得pshared的的非零值将使信号的进程间信号。 然而,这是错误的。 你应该继续读书,你就会明白,你必须找到一个共享内存区域的信号。 要做到这一点,有几个功能,可以作为你可以看到下面:

如果的pshared为非零,则信号量进程之间共享的,并且应该位于共享存储器的区域(见的shm_open(3),MMAP(2),和shmget的(2))。 (由于由叉创建子(2)继承父的存储器映射,它也可以访问信号量。),其可以访问共享存储器区可以使用sem_post(3),sem_wait(3)等的信号操作的任何过程。

我发现这种方法是比别人更复杂的方法,因此,我想鼓励人们使用sem_open()代替sem_init()

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

  • 如何分配共享内存,并使用派生的进程间共享变量。
  • 如何在共享存储区域初始化一个信号量,并且用于通过多个进程。
  • 如何派生多个进程,使家长等待,直到所有的孩子退出。
#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);
    }
}

OUTPUT

./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()函数失败多次,有一个共享变量shmkey值为-1将驻留在导致一个影响另一个改变共享内存的同一区域。 因此,程序执行将变得混乱。 为了避免这种情况,最好是检查,如果ftok()返回-1与否(更好的源代码检查,而不是打印到屏幕上像我一样,虽然我想告诉你万一有冲突的键值) 。

注意信号灯是如何声明和初始化。 这比你在的问题做了什么(不同sem_t sem VS sem_t* sem )。 此外,因为它们出现在这个例子中,你应该使用它们。 您不能定义sem_t*在使用sem_init()



Answer 2:

Linux的最小匿名sem_init + mmap MAP_ANONYMOUS例子

我喜欢这种设置的,因为它没有任何污染全局命名空间sem_open一样。

唯一的缺点是, MAP_ANONYMOUS不是POSIX的,我不知道任何替换: 匿名共享内存? shm_open例如需要一个全局标识符俨然sem_open

main.c中:

#define _GNU_SOURCE
#include <assert.h>
#include <semaphore.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char **argv) {
    pid_t pid;
    typedef struct {
        sem_t sem;
        int i;
    } Semint;

    Semint *semint;
    size_t size = sizeof(Semint);
    semint = (Semint *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
    assert(semint != MAP_FAILED);
    /* 1: shared across processes
     * 0: initial value, wait locked until one post happens (making it > 0)
     */
    sem_init(&semint->sem, 1, 0);
    semint->i = 0;
    pid = fork();
    assert(pid != -1);
    if (pid == 0) {
        sleep(1);
        semint->i = 1;
        msync(&semint->sem, size, MS_SYNC);
        sem_post(&semint->sem);
        exit(EXIT_SUCCESS);
    }
    if (argc == 1) {
        sem_wait(&semint->sem);
    }
    /* Was modified on the other process. */
    assert(semint->i == 1);
    wait(NULL);
    sem_destroy(&semint->sem);
    assert(munmap(semint, size) != -1);
    return EXIT_SUCCESS;
}

编译:

gcc -g -std=c99 -Wall -Wextra -o main main.c -lpthread

与运行sem_wait

./main

不运行sem_wait

./main 1

如果没有这种同步的assert很可能会失败,因为孩子睡一点整秒:

main: main.c:39: main: Assertion `semint->i == 1' failed.

经测试在Ubuntu 18.04。 GitHub的上游 。



文章来源: How to use POSIX semaphores on forked processes in C?