C,如何使用POSIX信号上派生进程? [英] C, how to use POSIX semaphores on forked processes?

查看:165
本文介绍了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))。 (由于通过叉创建一个子(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:


  • 如何分配共享内存,并使用共享变量之间的分叉
    流程。

  • 如何在共享存储区域初始化一个信号,并用于
    被多个进程。

  • 如何派生多个进程,使家长等待,直到所有的
    它的孩子退出。

#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 */
    sem_unlink ("pSem");      
    /* unlink prevents the semaphore existing forever */
    /* if a crash occurs during the execution         */
    printf ("semaphores initialized.\n\n");


    /* fork child processes */
    for (i = 0; i < n; i++){
        pid = fork ();
        if (pid < 0)              /* check for error      */
            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_destroy (sem);
        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与否(更好地源$ C ​​$ C检查,而不是打印到屏幕像我一样,虽然我想告诉你万一有冲突的关键值)。

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 SEM VS sem_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天全站免登陆