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

时间:2013-06-24 17:19:48

标签: c linux semaphore

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

3 个答案:

答案 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共享内存对象)。