是什么导致程序在反汇编视图中的系统调用后卡住?

What causes a program to stuck after a syscall in disassembler view?

我正在使用 QT-Creator 开发桌面应用程序。该项目在 C 中编程并在 gtest 中测试。它 运行 在 Linux (Debian) 机器上。

在尝试 运行 google 测试时,有时它会卡在反汇编代码中,如下所示:

0x7ffff780e16b                   0f 94 c0           sete   %al
0x7ffff780e16e                   eb 0f              jmp    0x7ffff780e17f
0x7ffff780e170                   be 80 00 00 00     mov    [=10=]x80,%esi
0x7ffff780e175                   45 30 c0           xor    %r8b,%r8b
0x7ffff780e178                   b8 ca 00 00 00     mov    [=10=]xca,%eax
0x7ffff780e17d                   0f 05              syscall
0x7ffff780e17f                   8b 3c 24           mov    (%rsp),%edi

每次卡住都是在反汇编代码的最后一行找的。我不知道它是关于什么的,但显然它与 syscall 有关。我试图跳转到其等效的 C 代码,但没有。我认为它与启动代码有关,但我不确定。

知道什么会导致这个问题以及我必须检查哪些方向吗?

注意:我的项目是多线程项目,里面使用了很多指针。我尽力用互斥体保护共享资源。

这可能是一个僵局。尝试使用 -fsanitize=thread 或 运行 使用 valgrind 进行编译。你必须调试它;检查 运行ning 线程的堆栈跟踪以查看程序正在尝试获取哪个互斥量,以及哪些互斥量已从何处锁定。 Valgrind 和 -fsanitize=thread 都能够检测 一些 死锁情况。 运行 您使用 valgrind 编写的程序既快速又简单,如果显示错误,那是您的幸运;如果没有,您将不得不求助于“正确的”调试技术。

根据要求,关于具体情况的一些细节:

反汇编显示了对 futex() 函数的系统调用。此函数阻塞当前线程,直到某个条件为真。如果没有其他线程设置此条件,则该线程将无限期阻塞。所以实际发生的是线程挂起 inside syscall,即在内核级别。调试器是在告诉你它在 syscall 之后阻塞了 ,因为它不知道内核内部发生了什么。 std::mutex 等标准线程同步机制在内部调用 futex(),因此使用这些机制将导致您的代码调用 futex().

这是一个非常简单的例子,其中 threadFun 函数总是在 futex() 系统调用中挂起:

#include <thread>
#include <mutex>

void threadFun (std::mutex& m) {
    m.lock ();
}


int main () {
    std::mutex m;

    m.lock ();

    std::thread t (threadFun, std::ref(m));

    t.join ();
}

当运行在本地通过GDB将其挂起并中断程序时,可以清楚地看到反汇编内部构成futex()调用的mov [=21=]xca,%eaxsyscall指令。该程序将永远挂起,直到被杀死。

futex()syscall 指令如何在生成的代码中结束?

C 或 C++ 标准库也可能包含(内联)汇编代码。在这种情况下,std::mutex 调用 pthread 函数 pthread_mutex_lock which is implemented in the glibc here, which calls __lll_lock_wait which instantiates lll_futex_timed_wait which in turn instantiates internal_syscall4,它使用内联汇编生成 syscall 指令。由于您可能没有安装 glibc 调试符号和源代码,调试器看不到这段代码,只能向您展示反汇编。

但是,所有这些细节可能对您的调试帮助不大。当中断 GDB 中的示例程序时,回溯显示从 threadFun 中调用 std::mutex::lock(),这就是您真正需要知道的是死锁:程序请求等待从未发生的事情, 运行时间环境(即 C++ 标准库 + glibc + 内核)符合。它究竟如何做到这一点并不重要。如果你写 std::puts(nullptr) 你会崩溃,而这究竟是如何发生的对于应用程序开发来说并不是很重要;您必须在 您的 代码中找到错误调用。

当您发现您的代码在 std::mutex::lock() 等锁定函数中挂起时,您知道这可能是一个死锁,然后可以使用上述工具,例如 -fsanitize=thread 和 [=37= 】 寻找真正的原因。如果这些没有帮助,请使用 GDB 命令 info threadsthread X 以及 bt 来获取所有线程的回溯。通过检查这些,您应该能够找到哪个线程锁定了哪个互斥体。