Linux 如何在 C 中的分叉进程上使用 POSIX 信号量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16400820/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use POSIX semaphores on forked processes in C?
提问by Varaquilex
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);
}
And the output is:
输出是:
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?
这显然意味着信号量没有按预期工作。你能解释一下我应该如何在分叉进程上使用信号量吗?
采纳答案by Varaquilex
The problem you are facing is the misunderstanding of sem_init()
function. When you read the manual pageyou will see this:
你面临的问题是对sem_init()
函数的误解。当您阅读手册页时,您将看到:
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 的非零值将使信号量成为进程间信号量。然而,这是错误的。你应该继续阅读,你会明白你必须在共享内存区域中定位信号量。为此,可以使用几个函数,如下所示:
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.
如果 pshared 不为零,则信号量在进程之间共享,并且应位于共享内存区域(请参阅 shm_open(3)、mmap(2) 和 shmget(2))。(因为由 fork(2) 创建的子进程继承其父进程的内存映射,它也可以访问信号量。)任何可以访问共享内存区域的进程都可以使用 sem_post(3)、sem_wait(3) 等操作信号量.
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()
.
我发现这种方法是比别人更复杂的方法,因此,我想鼓励人们使用sem_open()
替代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);
}
}
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.
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).
检查也不错,shmkey
因为ftok()
失败时返回-1。但是,如果您有多个共享变量并且ftok()
函数多次失败,则具有shmkey
带值的共享变量-1
将驻留在共享内存的同一区域中,从而导致一个更改影响另一个。因此程序执行会变得混乱。为了避免这种情况,最好检查是否ftok()
返回 -1(最好检查源代码而不是像我那样打印到屏幕上,尽管我想向您展示关键值以防发生冲突)。
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()
.
注意信号量是如何声明和初始化的。这与您在问题(sem_t sem
vs sem_t* sem
)中所做的不同。此外,您应该按照本示例中出现的方式使用它们。您不能sem_t*
在sem_init()
.