【问题标题】:Does executing an int 3 interrupt stop the entire process on Linux or just the current thread?执行 int 3 中断会停止 Linux 上的整个进程还是仅停止当前线程?
【发布时间】:2014-04-18 05:10:43
【问题描述】:

假设架构是 x86。操作系统是基于 Linux 的。给定一个单线程执行int 3指令的多线程进程,中断处理程序是停止执行整个进程还是只停止执行int 3指令的线程?

【问题讨论】:

  • 自己测试这个不是很容易吗,例如在 GDB 中?我假设使用 int 3s 来触发中断。
  • @Rup: ptrace 有点改变。虽然可以观察默认行为,但在 GDB 中,可以选择在到达断点时停止所有线程,或者在其余线程继续在后台运行时停止单个线程。从这个意义上说,在 GDB 中无法得到明确的答案。

标签: c++ c linux multithreading interrupt-handling


【解决方案1】:

答案确实不是。 Int 3 用于触发断点。中断处理程序很小,中断及其处理程序都不会停止任何线程。

如果没有加载调试器,处理程序要么忽略它,要么调用操作系统采取某种错误操作,比如发出信号(可能是 SIGTRAP)。没有线程受到损害。

如果有一个进程内调试器,断点 ISR 会将控制权转移给它。断点不会停止任何线程,除了中断的线程。调试器可能会尝试挂起其他人。

如果存在进程外调试器,处理程序将调用它,但这必须通过操作系统进行调解才能进行合适的上下文切换。作为该切换的一部分,操作系统将暂停调试对象,这意味着它的所有线程都将停止。

【讨论】:

    【解决方案2】:

    由于问题是特定于 Linux 的,让我们深入了解内核源代码!我们知道int 3 将生成一个SIGTRAP,正如我们在do_int3 中看到的那样。 default behaviour of SIGTRAP 是终止进程并转储内核。

    do_int3 调用 do_trap,经过大量间接调用后,调用 complete_signal,大部分魔法都发生在这里。在 cmets 之后,无需太多解释就可以清楚地看到正在发生的事情:

    • 找到一个线程来传递信号。主线程被给予第一次破解,但任何线程都可以得到它,除非明确声明它不想这样做。
    • SIGTRAP 是致命的(我们假设我们想要确定默认行为是什么)并且必须转储核心,所以它对整个组都是致命的
    • 第 1003 行的循环唤醒所有线程并传递信号。

    编辑:回答评论:

    当进程是ptraced 时,该行为在manual page 中有很好的记录(请参阅“信号传递停止”)。基本上,内核选择一个任意线程处理信号后,如果选择的线程被跟踪,它进入signal-delivery-stop——这意味着信号还没有传递给进程,并且可以被跟踪进程抑制.调试器就是这种情况:在调试时,死进程对我们没有用(这并不完全正确,但让我们考虑实时调试场景,这是唯一在这种情况下有意义的场景),所以默认情况下除非用户另有说明,否则我们会阻止 SIGTRAP。在这种情况下,被跟踪进程如何处理 SIGTRAP(SIG_IGN 或 SIG_DFL 或自定义处理程序)无关紧要,因为它永远不会知道它发生了。

    请注意,在 SIGTRAP 的情况下,跟踪器进程必须考虑除正在停止的进程之外的各种情况,如手册页中每个 ptrace 操作下的详细说明。

    【讨论】:

    • SIGTRAP 的默认行为是终止进程并转储核心。您能否补充一下如果该进程已使用 ptrace (... TRACEME... )?使用 ptrace (...SEIZE...) 跟踪单个线程的情况如何?
    • 也许我会通过深入研究文档来找到这一点,但是您说过 在内核选择一个处理信号的任意线程之后,如果跟踪所选线程,它就会进入信号传递停止。你没有说的是如果所选线程不是 ptrace'd 并且包含已选择线程的进程包含 ptrace'd 的线程会发生什么。
    • signal-delivery-stop 的逻辑在“交付”端,而不是“生成”端,参见do_signal() -> get_signal(),检查 current->ptrace标志,然后调用 ptrace_signal() -> ptrace_stop(),它会暂停当前,并向父级发出信号。
    【解决方案3】:

    int 3 是不允许运行用户空间代码的特权指令。

    内核随后会向您的进程发送一个 SIGTRAP 信号,而 SIGTRAP 信号的默认操作是终止整个进程。

    【解决方案4】:

    很容易测试:

    #include <thread>
    #include <vector>
    
    void f(int v) {
        std::this_thread::sleep_for(std::chrono::seconds(2));
        if (v == 2) asm("int $3");
        std::this_thread::sleep_for(std::chrono::seconds(1));
        printf("%d\n", v); // no sync here to keep it simple
    }
    
    int main() {
        std::vector<std::thread> threads;
        for (int i = 0; i < 4; i++) threads.emplace_back(f, i);
        for (auto& thread : threads) thread.join();
        return 0;
    }
    

    如果只停止线程,它仍应打印来自 2 以外的线程的消息,但情况并非如此,整个进程在打印任何内容之前停止(或在调试时触发断点)。在 Ubuntu 上,您收到的消息是:

    跟踪/断点陷阱(核心转储)

    【讨论】:

    • 好的,其他的thresds继续运行吗?
    • @NiklasB。不,一切都停止了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-30
    • 1970-01-01
    相关资源
    最近更新 更多