C++ 在Linux上执行INT3中断是停止整个进程还是仅停止当前线程?

C++ 在Linux上执行INT3中断是停止整个进程还是仅停止当前线程?,c++,c,linux,multithreading,interrupt-handling,C++,C,Linux,Multithreading,Interrupt Handling,假设架构是x86。操作系统是基于Linux的。给定一个单线程执行int 3指令的多线程进程,中断处理程序是停止执行整个进程,还是只停止执行int 3指令的线程?很容易测试: #include <thread> #include <vector> void f(int v) { std::this_thread::sleep_for(std::chrono::seconds(2)); if (v == 2) asm("int $3"); std::

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

很容易测试:

#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;
}
#包括
#包括
空f(整数v){
std::this_thread::sleep_for(std::chrono::seconds(2));
如果(v==2)asm(“整数$3”);
std::this_thread::sleep_for(std::chrono::seconds(1));
printf(“%d\n”,v);//此处不同步以保持简单
}
int main(){
向量线程;
对于(inti=0;i<4;i++)线程;
对于(自动线程和线程:线程)thread.join();
返回0;
}
如果只有线程被停止,它仍然应该打印来自线程2以外的其他线程的消息,但情况并非如此,整个进程在打印任何内容之前停止(或者在调试时触发断点)。在Ubuntu上,您得到的信息是:

跟踪/断点陷阱(堆芯转储)


因为这个问题是Linux特有的,所以让我们深入了解内核源代码!我们知道
int3
将生成SIGTRAP,如中所示。其目的是终止进程并转储内核

do_int3
调用,经过大量间接操作后,调用,大部分魔法都发生在这里。在这些评论之后,我们可以清楚地看到正在发生的事情,而不需要太多解释:

  • 找到一个线程将信号传递给。主线程被赋予第一条裂纹,但任何线程都可以得到它,除非明确声明它不想得到
  • SIGTRAP是致命的(我们假设我们想要确定默认行为是什么),并且必须转储核心,因此它对整个组是致命的
  • 第1003行的循环唤醒所有线程并发送信号
编辑:回答评论:

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


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

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

如果没有加载调试器,处理程序将忽略它或调用操作系统来执行某种错误操作,如发出信号(可能是SIGTRAP)。没有线被损坏

如果存在进程内调试器,则断点ISR将控制权转移给它。断点不会停止任何线程,但中断的线程除外。调试器可能会尝试挂起其他调试器


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

int 3是一条特权指令,不允许运行用户空间代码


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

您自己(例如在GDB中)测试这一点不是很容易吗?我假设使用int 3s触发中断。@Rup:
ptrace
稍微改变了一些事情。虽然可以观察默认行为,但在GDB中,可以选择在到达断点时停止所有线程,或者在其余线程继续在后台运行时停止单个线程。从这个意义上说,在GDB中无法获得明确的答案。那么其他thresd是否继续运行?@NiklasB。不,一切都停止了。SIGTRAP的默认行为是终止进程并转储内核。如果这个过程是从ptrace(…TRACEME…)开始的,您能补充一下会发生什么吗?如果使用ptrace(…jack…)跟踪单个线程,情况会怎样?也许我会通过深入阅读文档来发现这一点,但您已经说过,在内核选择处理信号的任意线程之后,如果跟踪所选线程,它将进入信号传递停止。您没有说的是,如果所选线程不是ptrace'd,并且包含所选线程的进程包含ptrace'd线程,那么会发生什么。信号传递停止的逻辑位于“传递”端,而不是“生成”端,请参见->get_signal(),检查当前->ptrace标志,然后调用ptrace_signal()->ptrace_stop(),它暂停电流,并向父级发出信号。