#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;
}
答案 0 :(得分:3)
在fork()
之后,无法保证子进程在父进程之前运行,我很确定通常是父进程在fork()
之后继续在OS调度程序上执行。
信号量必须在共享内存中,如VoidPointer和QWR所建议的那样,并在man page中说明:
如果 pshared 非零,则信号量在进程之间共享,并且应位于共享内存的区域中(请参阅
shm_open(3)
,mmap(2)
和{{ 1}}。
您可以使用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()
,而只是让父母调用sem_wait()
,而不是同时调用0
。孩子不等待,但在完成后调用sem_wait()
,这将唤醒父母。
答案 1 :(得分:2)
fork
父和子将在完全不同的区域包含不同的sem
变量。做sem_wait(&sem)
会在父母和孩子中引用完全不同的记忆,因此两者都在获取信号量。
如果您想要在不同进程之间进行信号量处理,那么您应该使用sem_init here手册页和开启者页面is here中提到的shared memory
。
答案 2 :(得分:1)
http://linux.die.net/man/7/sem_overview 进程共享信号量必须放在共享内存区域(例如,使用shmget(2)创建的System V共享内存段,或使用shm_open(3)创建的POSIX共享内存对象)。