3
#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;
}
4

3 回答 3

3

在 a 之后fork(),不能保证子进程在父进程之前运行,而且我很确定通常是父进程在fork().

正如 VoidPointer 和 QWR 所建议的,信号量必须在共享内存中,并在手册页中说明:

如果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()“同时”调用,这意味着谁先到达那里,谁就会先获得它。

sem_wait()您可以将信号量初始化为,而不是同时调用,0并且只让父调用sem_wait()。孩子不等待,而是sem_post()在完成后调用,这将唤醒父母。

于 2013-06-24T17:22:40.733 回答
2

fork父母和孩子将sem在完全不同的区域包含不同的变量之后。做sem_wait(&sem)将在父母和孩子中引用完全不同的记忆,因此两者都在获取信号量。

如果你想在不同进程之间使用信号量,那么你应该使用这里shared memory的 sem_init 手册页中提到的,这里开眼界的页面。

于 2013-06-24T17:26:16.787 回答
1

http://linux.die.net/man/7/sem_overview 进程共享信号量必须放在共享内存区域中(例如,使用 shmget(2) 创建的 System V 共享内存段,或 POSIX 共享内存对象使用 shm_open(3) 创建的)。

于 2013-06-24T17:26:00.450 回答