【问题标题】:Linux futex syscall spurious wakes with return value 0?Linux futex syscall 虚假唤醒,返回值为 0?
【发布时间】:2011-11-14 21:37:41
【问题描述】:

我遇到了 Linux futex 系统调用(FUTEX_WAIT 操作)的问题,有时看似无故提前返回。文档指定了可能导致它提前返回的某些条件(没有FUTEX_WAKE),但这些都涉及非零返回值:EAGAIN 如果 futex 地址处的值不匹配,ETIMEDOUT 用于定时等待超时,EINTR 被(非重新启动)信号等中断时。但我看到返回值为 0。除了FUTEX_WAKE 或终止其set_tid_address 指针指向futex,可能导致FUTEX_WAIT 返回值为0?

如果有用,我正在等待的特定 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 返回之后的线程中。

【问题讨论】:

  • 我想我们可能需要看一个最小的例子;很难提出实质性的建议,因为很多东西都是未知的(无论如何我都会将我的一个预感作为临时答案发布,因为它对于评论来说太大了)
  • 确实,我会看看我是否可以整理一个最小的例子。
  • 嗯,我认为手册页很不清楚。 FUTEX_WAIT 返回值下的条件将非零条件限定为 error 条件,而不仅仅是诊断。然后它说“如果发生错误,所有操作都返回 -1,并设置 errno 以指示错误。”另一方面,这里的条件不会在 ERRORS 部分中重复。
  • 我刚刚与strace 确认,当FUTEX_WAIT 返回时,“子线程”尚未调用_exit
  • 这可能值得在 linux 内核邮件列表中询问。

标签: c linux futex


【解决方案1】:

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

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

【讨论】:

  • 如果tid 在调用FUTEX_WAIT 时还没有写入tid 值,则操作将返回EAGAIN 而不是0。(无论如何,整个点CLONE_PARENT_SETTID 标记到clone 是为了确保在任一线程能够执行之前已经写入了值。)我认为用户空间中没有任何竞争的可能性,因为用户空间中没有发生任何有趣的事情......
猜你喜欢
  • 2020-04-21
  • 2011-06-07
  • 2012-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-19
相关资源
最近更新 更多