如何在C中的分叉进程上使用POSIX信号量? [英] How to use POSIX semaphores on forked processes in C?

查看:115
本文介绍了如何在C中的分叉进程上使用POSIX信号量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想派生多个进程,然后在它们上使用信号灯.这是我尝试过的:

I want to fork multiple processes and then use a semaphore on them. Here is what I tried:

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.

这显然意味着信号灯没有按预期的方式工作.您能解释一下我应该如何在分支进程上使用信号量吗?

This clearly means the semaphore didn't work as it was supposed to. Can you explain how I should use semaphores on forked processes?

推荐答案

您面临的问题是对sem_init()函数的误解.当您阅读手册页时 您将看到以下内容:

The problem you are facing is the misunderstanding of sem_init() function. When you read the manual page you will see this:

pshared参数指示是否要共享此信号量 在进程的线程之间,或在进程之间.

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

如果您已经读完了这一点,您将认为pshared的非零值将使信号量成为进程间信号量.但是,这是错误的. 您应该继续阅读,您将了解到必须在共享内存区域中找到信号灯.为此,可以将几个功能用作 您可以在下面看到:

If you are done reading up to this point, you will think that the non-zero value of pshared will make the semaphore inter-process semaphore. However, this is wrong. You should continue reading and you'll understand that you have to locate the semaphore in a shared memory region. To do that, several functions can be used as you can see below:

如果pshared不为零,则信号在进程之间共享, 并应位于共享内存的区域中(请参见shm_open(3), mmap(2)和shmget(2)). (由于fork(2)创建的子项继承 其父级的内存映射,它也可以访问该信号量.) 可以访问共享内存区域的进程可以在 使用sem_post(3),sem_wait(3)等的信号量.

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().

I find this approach as a more complicated approach than others, therefore I want to encourage people to use sem_open() instead of sem_init().

下面您将看到一个完整的程序,说明了以下内容:

Below you can see a complete program illustrates the following:

  • 如何在分叉之间分配共享内存和使用共享变量 流程.
  • 如何在共享内存区域中初始化信号量并使用 通过多个过程.
  • 如何派生多个进程并使父进程等待所有 它的孩子退出.
  • How to allocate shared memory and use shared variables between forked processes.
  • How to initialize a semaphore in a shared memory region and is used by multiple processes.
  • How to fork multiple processes and make the parent wait until all of its children exit.
#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()函数多次失败,则具有shmkey且值为-1的共享变量将驻留在同一变量中 共享内存的区域导致一个更改影响另一个.因此,程序执行将变得混乱.为避免这种情况,最好检查ftok() 是否返回-1(最好是签入源代码,而不是像我一样在屏幕上打印,尽管我想向您显示键值以防发生冲突).

It is not bad to check shmkey since when ftok() fails, it returns -1. However if you have multiple shared variables and if the ftok() function fails multiple times, the shared variables that have a shmkey with value -1 will reside in the same region of the shared memory resulting in a change in one affecting the other. Therefore the program execution will get messy. To avoid this, it is better checked if the ftok() returns -1 or not (better to check in source code rather than printing to screen like I did, although I wanted to show you the key values in case there is a collision).

请注意如何声明和初始化信号量.它与您在问题中所做的不同(sem_t semsem_t* sem).此外,您应该使用它们在本示例中显示的形式.您不能定义sem_t*并在sem_init()中使用它.

Pay attention to how the semaphore is declared and initialized. It's different than what you have done in the question (sem_t sem vs sem_t* sem). Moreover, you should use them as they appear in this example. You cannot define sem_t* and use it in sem_init().

这篇关于如何在C中的分叉进程上使用POSIX信号量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆