POSIX信号量:为什么父进程会在子进程发布之前获取信号量?

#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), and 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(),这将唤醒父母.

上一篇:应用程序退出后,信号量仍保持打开状态


下一篇:并发编程 Semaphore的使用和详解