一尘不染

如何在C中的分叉进程上使用POSIX信号量?

linux

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

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);
}

输出为:

孩子(0)分叉
孩子(1)分叉
  Child(0)在关键部分。
  Child(1)在关键部分。
孩子(2)分叉
  Child(2)在关键部分。
孩子(3)分叉
  Child(3)在关键部分。
孩子(4)分叉
  Child(4)在关键部分。
  Child(0)新值* p = 0。
  Child(1)新值* p = 1。
  Child(2)的新值* p = 3。
  Child(3)的新值* p = 3。

  Child(4)的新值* p = 4。
父母:所有孩子都退出了。

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


阅读 209

收藏
2020-06-02

共1个答案

一尘不染

您面临的问题是对sem_init()功能的误解。阅读手册页时, 您将看到以下内容:

pshared参数指示此信号量是在进程的线程之间还是在进程之间共享。

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

如果pshared为非零,则信号量在进程之间共享,并且应位于共享内存的区域中(请参见shm_open(3),mmap(2)和shmget(2))。(由于fork(2)创建的子级继承了其父级的内存映射,因此它也可以访问该信号量。)任何可以访问共享内存区域的进程都可以使用sem_post(3),sem_wait(3)等对该信号量进行操作。

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

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

  • 如何在派生的进程之间分配共享内存和使用共享变量。
  • 如何在共享内存区域中初始化信号量,并由多个进程使用。
  • 如何派生多个进程并使父级等待所有子级退出。

    include / printf() /

    include / exit(), malloc(), free() /

    include / key_t, sem_t, pid_t /

    include / shmat(), IPC_RMID /

    include / errno, ECHILD /

    include / sem_open(), sem_destroy(), sem_wait().. /

    include / 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()函数多次失败,则具有shmkeywith值的共享变量-1将驻留在共享内存的同一区域中,从而导致一个更改影响另一个。因此,程序执行将变得混乱。为了避免这种情况,最好检查是否ftok()
返回-1(最好是签入源代码,而不是像我一样打印到屏幕上,尽管我想向您显示键值以防发生冲突)。

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

2020-06-02