为什么我的分叉进程上的信号量没有被释放?

Why is my semaphore on a forked process not being released?

我在使用 POSIX 信号量释放分叉进程时遇到问题。通过在 forkexec 之后调用 运行 进程的另一个实例来启动分叉进程。有时 child 会被释放,有时不会。

它是一个名为 semaphore 的 POSIX 共享内存,奇怪的是它有时会起作用。我检查了其他解决方案,但他们的解决方案对我没有帮助。

void init()
{
    ...
    sem_unlink(sem_name.c_str());

    if (parent_process)
    {
        sem_t* semaphore = sem_open(sem_name.c_str(), O_CREAT | O_RDWR, 0);
        if (SEM_FAILED == semaphore)
        {
            display_error();
        }
        sem_close(semaphore);
    }

    child_pid = fork();

    if (child_pid == -1)
    {
        display_error();
    }
    else if (child_pid == 0)
    {
        int ret = execve(program_name, args, env);
        if (ret == -1)
        {
            display_error();
        }
    }
    else
    {
        // rest of code
    }
    ...
}

我让 child 进程在另一个具有此功能的 class 中等待释放:

void wait_until_released()
{
    if (!parent_process)
    {
        sem_t* semaphore = sem_open(sem_name.c_str(), O_CREAT | O_RDWR, 0);
        if (SEM_FAILED == semaphore)
        {
            display_error();
        }

        sem_wait(semaphore);

        sem_close(semaphore);
        sem_unlink(semaphore);            
    }
}

post 是在代码中的另一个位置完成的:

void release_child()
{
    sem_t* semaphore = sem_open(sem_name.c_str(), O_CREAT | O_RDWR, 0);
    if (SEM_FAILED == semaphore)
    {
        display_error();
    }

    if (sem_post(semaphore) != 0)
    {
        display_error();
    }

    sem_close(semaphore);
    sem_unlink(semaphore);
}

这个问题的发生最终是因为我在 POSIX 信号量上调用 sem_unlink,然后在分叉进程中对其进行等待。当所有文件描述符都对信号量调用 sem_close 时,调用 sem_unlink 会导致信号量被删除。从本质上讲,这阻止了我的 child 进程能够使用该实例并完全被释放。

这仅在某些时候有效,因为有一个基本假设,即在我们调用 release_child 时 child 已经在等待释放。这并不能保证,这是有时但并非一直有效的原因。如果我们在 child 调用 sem_wait 之前调用 release_child,那么我们将完全删除信号量,并且 child 创建他们自己的信号量版本,该版本永远不会得到 post编辑到。

通过将 unlink 调用移到第一组代码中的 if 语句之后,我阻止了 child 进程在等待信号量之前删除它。此外,通过从 release_childwait_until_released 函数中的 sem_open 中删除 O_CREAT 标志以及从 release_child 函数中删除 sem_unlink ,我阻止了child 从创建自己的信号量。

我想记录下我看到的行为,因为那才是真正给我带来问题的原因。在调试和修复这个问题的过程中,我了解到如果 parent 创建了信号量但没有关闭它,那么 child 正在调用 sem_unlink 并创建它自己的版本一样的名字。这让我相信原来的信号量仍然存在,但 sem_post and/or sem_wait 不工作。

因此,在执行信号量时,请注意 post、wait、close 和 unlink 调用。尤其是涉及到分叉进程时!!