6

我遇到了 Linuxfutex系统调用(FUTEX_WAIT操作)的问题,有时似乎无缘无故地提前返回。文档指定了可能导致它提前返回(没有 a FUTEX_WAKE)的某些条件,但这些都涉及非零返回值:EAGAIN如果 futex 地址处的值不匹配,则ETIMEDOUTfor timed 等待超时,EINTR当被 a (非重新启动)信号等。但我看到返回值为 0。除了指针指向 futexFUTEX_WAKE的线程的终止或终止之外,还有什么可能导致返回值为 0 的返回?set_tid_addressFUTEX_WAIT

如果它有用,我正在等待的特定 futex 是线程 tid 地址(由clone系统调用设置CLONE_CHILD_CLEARTID),并且线程没有终止。我的(显然是不正确的)假设FUTEX_WAIT返回 0 的操作只能在线程终止导致程序逻辑中的严重错误时发生,我已经通过循环和重试来修复它,即使它返回 0,但现在我很好奇为什么会这样。

这是一个最小的测试用例:

#define _GNU_SOURCE
#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <linux/futex.h>
#include <signal.h>

static char stack[32768];
static int tid;

static int foo(void *p)
{
        syscall(SYS_getpid);
        syscall(SYS_getpid);
        syscall(SYS_exit, 0);
}

int main()
{
        int pid = getpid();
        for (;;) {
                int x = clone(foo, stack+sizeof stack,
                        CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND
                        |CLONE_THREAD|CLONE_SYSVSEM //|CLONE_SETTLS
                        |CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
                        |CLONE_DETACHED,
                        0, &tid, 0, &tid);
                syscall(SYS_futex, &tid, FUTEX_WAIT, x, 0);
                /* Should fail... */
                syscall(SYS_tgkill, pid, tid, SIGKILL);
        }
}

让它运行一段时间,它最终应该以Killed( ) 终止,这只有在返回SIGKILL时线程仍然存在的情况下才有可能。FUTEX_WAIT

在任何人假设这只是内核在完成销毁线程之前唤醒 futex(这实际上可能发生在我的最小测试用例中)之前,请注意,在我的原始代码中,我实际上观察到用户空间代码在线程中运行FUTEX_WAIT回来后很好。

4

1 回答 1

0

您能否处理父操作或子操作是否先完成之间的竞争条件?您可能可以通过在 foo() 开头或在 clone() 之后立即放置小睡眠来研究这个理论,以确定事件的强制排序是否掩盖了问题。我不建议以这种方式修复任何东西,但它可能有助于调查。也许 futex 还没有准备好等待,直到孩子进一步完成初始化,但是父母的克隆有足够的能力返回给调用者?

具体来说,CLONE_VFORK 选项的存在似乎暗示这是一个危险的场景。您可能需要一种双向信号机制,以便孩子向父母发出信号,表明它已经足够远,可以安全地等待孩子。

于 2011-09-14T20:13:54.470 回答