POSIX 信号量:为啥父进程在子进程释放信号量之前获取信号量?
Posted
技术标签:
【中文标题】POSIX 信号量:为啥父进程在子进程释放信号量之前获取信号量?【英文标题】:POSIX semaphore: Why come the parent process acquire semaphore before child releases it?POSIX 信号量:为什么父进程在子进程释放信号量之前获取信号量? 【发布时间】:2013-06-24 17:19:47 【问题描述】:#include <semaphore.h>
int main(void)
int pfds[2];
char buff[20];
sem_t sem;
sem_init(&sem, 1, 1);
pipe(pfds);
if (!fork())
printf("Child: Waiting to acquire semaphore\n");
sem_wait(&sem);
printf("child acquires lock\n");
sleep(5);
write(pfds[1], "Hello", 6); /* make stdout same as pfds[1] */
close(pfds[0]); /* we don't need this */
printf("child releases lock\n");
sem_post(&sem);
else
printf("Parent: Waiting to acquire semaphore\n");
sem_wait(&sem);
printf("Parent acquires lock\n");
read(pfds[0],buff,6); /* we don't need this */
printf("Parent read: %s",buff);
printf("parent releases lock\n");
sem_post(&sem);
sem_destroy(&sem);
return 0;
上面是我创建的一个简单管道,孩子在其中写入,父母在其中读取。我已经把一个信号量变量初始化为 1,所以当孩子写的时候,父母应该等待。 添加了故意的“睡眠”,以便查看旋转“父:等待获取信号量”的父进程。
The expected sequence should be:
Child: Waiting to acquire semaphore
child acquires lock...(delay of 5 secs here)
child releases lock
Parent: Waiting to acquire semaphore
Parent acquires lock
Parent read..bla-bla
parent releases lock
However it happens:
[root@localhost interview]# ./a.out
Child: Waiting to acquire semaphore
child acquires lock
Parent: Waiting to acquire semaphore
Parent acquires lock -> NOTE: Parent acquires lock before child releases it
child releases lock
Parent read: Hello
parent releases lock
问题是:为什么 parent 获得锁而 child 延迟并仍然持有它,但尚未释放信号量?
另外,有什么方法可以确保孩子总是首先获得信号量,因为它应该写入管道?
编辑:感谢@jxh。这是修改后的代码,效果很好(粘贴供大家参考)。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/mman.h>
int main(void)
int pfds[2];
char buff[20];
sem_t *sem = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
sem_init(sem, 1, 0);
pipe(pfds);
if (!fork())
write(pfds[1], "Hello", 6); /* make stdout same as pfds[1] */
close(pfds[0]); /* we don't need this */
printf("child releases semaphore\n");
sem_post(sem);
else
printf("Parent: Waiting to acquire semaphore\n");
sem_wait(sem);
printf("Parent acquires lock\n");
read(pfds[0],buff,6); /* we don't need this */
printf("Parent read: %s\n",buff);
printf("parent releases lock\n");
sem_post(sem);
sem_destroy(sem);
return 0;
【问题讨论】:
【参考方案1】:在fork()
之后,无法保证子进程在父进程之前运行,我很确定在fork()
之后继续在操作系统调度程序上执行的通常是父进程。
按照 VoidPointer 和 QWR 的建议,信号量必须位于共享内存中,并在 man page 中声明:
如果 pshared 不为零,则信号量在进程之间共享,并且应该位于共享内存区域中(请参阅
shm_open(3)
、mmap(2)
和shmget(2))
。
您可以像这样使用mmap()
在共享内存中分配信号量:
sem_t *sem = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
assert(sem != MAP_FAILED);
您已将信号量初始化为1
,这允许对sem_wait()
的第一次调用成功。在您的代码中,父母和孩子都尝试在“同一时间”调用sem_wait()
,这意味着谁先到达那里,谁就先获得它。
您可以将信号量初始化为0
,而不是同时调用sem_wait()
,并且只让父级调用sem_wait()
。子进程不等待,完成后调用sem_post()
,这会唤醒父进程。
【讨论】:
但是为什么父级在子级释放之前获得锁呢?很奇怪 @kingsmasher1:您的父母和孩子正在竞相同时获取信号量。这不是你想要的。将信号量初始化为0
,这样父级将阻止等待子级。移除孩子对sem_wait()
的调用。
我会讲一件有趣的事情。你知道,同样的代码只是将信号量初始化为“0”会导致死锁。父母和孩子都打印:“等待获取..”并继续等待:-)
@kingsmasher1:请仔细阅读我的评论,并注意我编辑答案中突出显示的部分。
@jxh 这个页面pubs.opengroup.org/onlinepubs/7908799/xsh/sem_init.html 说If the pshared argument has a non-zero value, then the semaphore is shared between processes; in this case, any process that can access the semaphore sem can use sem for performing sem_wait(), sem_trywait(), sem_post(), and sem_destroy() operations.
说只有当两个进程都可以访问sem 时才在进程之间共享,这里不是这种情况。【参考方案2】:
在fork
之后,父和子将在完全不同的区域包含不同的sem
变量。执行sem_wait(&sem)
将在父母和孩子中引用完全不同的记忆,因此两者都在获取信号量。
如果你想在不同进程之间使用信号量,那么你应该使用shared memory
,如 sem_init 的手册页 here 和令人大开眼界的页面 is here 中所述。
【讨论】:
@jxh(还有 at-me!):POSIX 页面似乎相当模糊,它可以解释为“用户必须将 sem 链接到共享区域”或“系统将分配共享区域和在 sem 中指出这一点”。看起来各种手册页不那么模棱两可了。 @torek:是的,我最初也有同样的直觉反应,但改变我想法的是“应该”在共享内存中。我想如果手册指定如何更改信号量,它会说“将”在共享内存中。【参考方案3】:http://linux.die.net/man/7/sem_overview 进程共享信号量必须放置在共享内存区域中(例如,使用 shmget(2) 创建的 System V 共享内存段,或使用 shm_open(3) 创建的 POSIX 共享内存对象)。
【讨论】:
@torek:你确定。手册页似乎表明信号量应该已经在共享内存中,并且如果信号量未共享(在我的答案中以 cmets 表示),则发布者报告的行为就像我所期望的那样。 @torek 嗨 torek。我认为他的问题是由于共享内存。他的逻辑似乎是正确的。 我在实现中四处寻找并认为它是自动共享的,然后我测试了......它不是。所以我错了。以上是关于POSIX 信号量:为啥父进程在子进程释放信号量之前获取信号量?的主要内容,如果未能解决你的问题,请参考以下文章