我想分叉多个进程,然后在它们上使用信号量。这是我试过的:
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.
这显然意味着信号量不能像预期的那样工作。你能解释我应该如何在分叉进程上使用信号量吗?
答案 0 :(得分:59)
您面临的问题是对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
的共享变量将驻留在同一个函数中
共享内存的区域导致一个影响另一个的变化。因此程序执行会变得混乱。为避免这种情况,最好检查一下ftok()
返回-1或不返回(更好地检查源代码而不是像我一样打印到屏幕,虽然我想在出现碰撞时向您显示关键值)。
注意声明和初始化信号量的方式。它与您在问题中所做的不同(sem_t sem
vs sem_t* sem
)。此外,您应该使用它们,因为它们出现在此示例中。您无法定义sem_t*
并在sem_init()
中使用它。
答案 1 :(得分:1)
Linux最小匿名sem_init
+ mmap
MAP_ANONYMOUS
示例
我喜欢这种设置,因为它不会像sem_open
那样污染任何全局名称空间。
唯一的缺点是MAP_ANONYMOUS
不是POSIX,我不知道有什么替代:例如,Anonymous shared memory? shm_open
像sem_open
一样采用全局标识符。
main.c:
#define _GNU_SOURCE
#include <assert.h>
#include <semaphore.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv) {
pid_t pid;
typedef struct {
sem_t sem;
int i;
} Semint;
Semint *semint;
size_t size = sizeof(Semint);
semint = (Semint *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
assert(semint != MAP_FAILED);
/* 1: shared across processes
* 0: initial value, wait locked until one post happens (making it > 0)
*/
sem_init(&semint->sem, 1, 0);
semint->i = 0;
pid = fork();
assert(pid != -1);
if (pid == 0) {
sleep(1);
semint->i = 1;
msync(&semint->sem, size, MS_SYNC);
sem_post(&semint->sem);
exit(EXIT_SUCCESS);
}
if (argc == 1) {
sem_wait(&semint->sem);
}
/* Was modified on the other process. */
assert(semint->i == 1);
wait(NULL);
sem_destroy(&semint->sem);
assert(munmap(semint, size) != -1);
return EXIT_SUCCESS;
}
编译:
gcc -g -std=c99 -Wall -Wextra -o main main.c -lpthread
运行sem_wait
:
./main
不使用sem_wait
运行:
./main 1
如果没有这种同步,assert
很可能会失败,因为孩子睡了整整一秒钟:
main: main.c:39: main: Assertion `semint->i == 1' failed.
在Ubuntu 18.04上测试。 GitHub upstream。