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

输出是:

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

  孩子(4)* p = 4的新值。
家长:所有的孩子都退出了。 

这显然意味着信号量没有按照它应该的那样工作。 你能解释一下如何在分叉进程上使用信号量吗?

Solutions Collecting From Web of "C,如何在分叉进程上使用POSIX信号量?"

你面临的问题是对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 <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. 

ftok()失败的时候检查shmkey并不坏,它返回-1。 但是,如果您有多个共享变量,并且如果ftok()函数失败多次, shmkey具有值为-1shmkey的共享变量将驻留在共享内存的同一个区域中,从而导致影响另一个的共享变量。 因此程序执行会变得混乱。 为了避免这种情况,最好检查一下ftok()返回-1(更好的办法是检查源代码,而不是象我一样打印到屏幕上,尽管我想告诉你关键值以防碰撞) 。

注意信号是如何声明和初始化的。 这与你在问题中所做的不同( sem_t sem vs sem_t* sem )。 而且,你应该像在这个例子中那样使用它们。 你不能定义sem_t*并在sem_init()使用它。