我遇到了Linux futex系统调用(FUTEX_WAIT操作)的问题,有时候看起来很早就没有原因.文档指定了可能导致它提前返回的某些条件(没有a FUTEX_WAKE),但这些条件都涉及非零返回值:EAGAIN如果futex地址的值不匹配,则ETIMEDOUT定时等待超时,EINTR当被a(非但是我看到返回值为0.除了指针指向futex FUTEX_WAKE的线程的终止之外,返回值为0的原因是什么?set_tid_addressFUTEX_WAIT
如果它有用,我正在等待的特定futex是线程tid地址(由clonesyscall 设置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);
}
}
Run Code Online (Sandbox Code Playgroud)
让它运行一段时间,它应该最终以Killed(SIGKILL)终止,这只有在FUTEX_WAIT返回时线程仍然存在时才有可能.
在任何人开始假设这只是内核在完成销毁线程之前唤醒futex(实际上这可能发生在我的最小测试用例中),请注意在我的原始代码中,我实际观察到在线程中运行的用户空间代码FUTEX_WAIT回来之后好吧.
您是否可以处理父操作或子操作是否先完成之间的竞争条件?您可以通过在 foo() 的开头或在 clone() 之后立即放置小睡眠来研究这个理论,以确定事件的强制排序是否掩盖了问题。我不建议以这种方式修复任何问题,但进行调查可能会有所帮助。也许 futex 还没有准备好等待,直到子进程进一步完成初始化,但父进程的克隆有足够的空间返回给调用者?
具体来说,CLONE_VFORK 选项的存在似乎意味着这是一个危险的情况。您可能需要一种双向信号机制,以便子级向父级发出信号,表明它已经足够远,可以安全地等待子级了。