#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
问题是:如果孩子被延迟并仍然持有,并且尚未释放信号量,父母如何获得锁定?
另外,有没有办法确保孩子总是先获取信号量,因为它应该写入管道?
编辑:感谢@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;
}
解决方法:
在fork()之后,不能保证子进程在父进程之前运行,我很确定通常是父进程在fork()之后继续在OS调度程序上执行.
根据VoidPointer和QWR的建议,信号量必须在共享内存中,并在man page中说明:
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)
, andshmget(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(),这将唤醒父母.