2013-05-06 123 views
18

我想分叉多個進程,然後在它們上使用信號量。這裏是我的嘗試:C,如何在分叉進程上使用POSIX信號量?

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.

這顯然意味着,因爲它本來是要信號燈沒有工作。你能解釋我應該如何在分叉進程上使用信號量嗎?

回答

53

您面臨的問題是對sem_init()函數的誤解。當你讀了manual page 你會看到:

pshared的參數表示此信號燈是否要共享一個進程的線程之間 ,或進程之間。

如果讀完這一點,您會認爲pshared的非零值會使信號量進程間信號量。但是,這是錯誤的。 你應該繼續閱讀,你會明白你必須找到共享內存區域的信號量。爲了做到這一點,有幾個函數可以被用作 你可以看到下面:

如果的pshared爲非零,則信號量進程之間 共享,並應位於共享存儲器的區域(見的shm_open( 3), mmap(2)和shmget(2))。 (由於fork(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); 
    } 
} 

輸出

./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的共享變量將駐留在共享內存的同一個 區域中,從而導致影響另一個的區域發生更改。因此程序執行會變得雜亂無章。爲了避免這種情況,最好檢查一下是否返回-1(儘管我想告訴你關鍵值以防碰撞),檢查源代碼而不是像我一樣打印到屏幕。

注意如何聲明和初始化信號量。這與你在問題中所做的不同(sem_t sem vs sem_t* sem)。此外,你應該使用它們,因爲它們出現在這個例子中。您無法定義sem_t*並在sem_init()中使用它。

+1

問題是,有些手冊頁並不那麼明確。看看SUSv2(例如):'如果pshared參數的值非爲零,則信號量在進程之間共享;在這種情況下,任何可以訪問信號量sem的進程都可以使用sem來執行sem_wait(),sem_trywait(),sem_post()和sem_destroy()操作。「要理解應該使用共享內存更加困難! – 2013-06-26 15:33:22

+0

不幸的是,這花了我2個家庭作業才發現:)這是我認爲Linux所缺乏的:他們認爲人們已經知道事情,就好像他們是開發人員社區的一部分一樣。同樣,在我看來,它應該是解釋性的,不像那些手冊。 – Varaquilex 2013-06-26 17:51:25

+0

我認爲你應該從子進程中分離出共享內存,就像'在fork(2)之後,孩子繼承了附加的共享內存段'(man shmdt)。 – Chris 2013-12-02 11:42:40