进程何时获得SIGABRT(信号6)?


202

进程在C ++中获得SIGABRT的场景是什么?该信号是否始终来自进程内部,或者可以从一个进程发送到另一个进程?

有没有办法确定哪个进程正在发送此信号?


3
有几种方法。如果编写程序,最简单的方法是为SIGABRT注册一个信号处理程序,该信号处理程序将打印出该信息并在返回之前刷新其流。第二种最简单的方法是在strace中运行程序。第三种最简单的方法是确保程序在崩溃时生成核心文件,并通过核心转储进行查找。
Parthian Shot 2015年

Answers:


194

abort()发送呼叫过程的SIGABRT信号,这abort()基本上是如何工作的。

abort()通常由检测内部错误或严重破坏约束的库函数调用。例如,如果堆的内部结构被堆溢出损坏,malloc()将调用abort()


27
对我而言,在大多数情况下,SIGABRT是通过libc尝试调用free()未初始化/损坏的指针而发送的
grandrew'Jan

如果我在代码中的某个地方,从构造函数中掩埋了纯虚函数调用,那还能以SIGABRT信号结束吗?我问是因为我看到一个错误消息,指出我有一个纯虚拟呼叫,而下一行给了我SIGABRT消息,并且应用程序崩溃或被操作系统关闭。谢谢。
赫尔沃耶

2
在MacOS上,我们获得了SIGABRT来打开大约1000个文件句柄而不关闭它们。我们的测试不是模拟,而是使用更通用的读取器类型抽象了文件,该类型没有Close()方法,因此被遗忘了。虽然覆盖面很大。:rolleyes:
Zyl

51

SIGABRT如果发生严重错误,libc和其他库通常使用它来中止程序。例如,SIGABRT在检测到双释放或其他堆损坏的情况下,glibc发送一个。

同样,大多数assert实现都SIGABRT在断言失败的情况下使用。

此外,SIGABRT可以像任何其他信号一样从任何其他进程发送。当然,发送过程需要以同一用户或root用户身份运行。


49

您可以使用该kill(2)接口将任何信号发送到任何进程:

kill -SIGABRT 30823

30823是dash我开始的过程,因此我可以轻松找到要终止的过程。

$ /bin/dash
$ Aborted

Aborted输出显然是如何dash报道一个SIGABRT。

它可以直接使用被发送到任何过程kill(2)或方法可以通过将信号发送到本身assert(3)abort(3)raise(3)


17

通常在内存分配出现问题时发生。

当我的程序试图分配一个负大小的数组时,发生在我身上。


14

对于c ++,还有另一个简单原因。

std::thread::~thread{
    if((joinable ())
        std::terminate ();
}

即线程范围结束,但您忘记调用其中一个

thread::join();

要么

thread::detach();

7

GNU libc会/dev/tty在调用之前将有关某些致命状况的信息打印出来abort()(然后触发SIGABRT),但是如果您将程序作为服务运行,或者不在真正的终端窗口中运行,则这些消息可能会丢失,因为没有tty显示消息。

请参阅我关于将libc重定向到stderr而不是/ dev / tty的文章:

捕获libc错误消息,从/ dev / tty重定向


4

进程从自身获取SIGABRT的情况:Hrvoje提到了从ctor调用一个隐藏的纯虚函数以生成异常,为此我重新创建了一个示例。在这里,当构造d时,它首先调用其基类A ctor,并将内部指针传递给它自己。在表填充有效指针之前,A ctor会调用纯虚方法,因为尚未构建d。

#include<iostream>
using namespace std;
class A {
public:
 A(A *pa){pa->f();}
 virtual void f()=0;
};
class D : public A {
public:
 D():A(this){}
 virtual void f() {cout<<"D::f\n";}
};
int main(){
 D d;
 A *pa = &d;
 pa->f();
 return 0;
}

编译:g ++ -o aa aa.cpp

ulimit -c无限

运行:./ aa

pure virtual method called
terminate called without an active exception
Aborted (core dumped)

现在,让我们快速查看核心文件,并验证是否确实调用了SIGABRT:

gdb aa core

见规则:

i r
rdx            0x6      6
rsi            0x69a    1690
rdi            0x69a    1690
rip            0x7feae3170c37

验证码:

迪斯0x7feae3170c37

mov    $0xea,%eax  = 234  <- this is the kill syscall, sends signal to process
syscall   <-----

http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

234 sys_tgkill pid_t tgid pid_t pid int sig = 6 = SIGABRT

:)


2

就我而言,这是由于数组中索引等于数组长度的输入所致。

string x[5];

for(int i=1; i<=5; i++){

    cin>>x[i];

}

正在访问不存在的x [5]。


1

正如“ @sarnold”恰当地指出的那样,任何进程都可以向其他进程发送信号,因此,一个进程可以将SIGABORT发送给其他进程;在这种情况下,接收进程由于其自身的调整而无法区分其是否到来。内存等,或其他人“单播”发送给它。

在我工作的其中一个系统中,有一个死锁检测器,该检测器实际上通过给出心跳来检测进程是否由于某些任务而结束。如果不是,则它声明该进程处于死锁状态,并将SIGABORT发送给该进程。

我只是想与提出的问题分享这个预期。


0

我将从竞争性编程中给出我的答案(cp)角度,但它也适用于其他领域。

在执行cp时,约束很多次。

例如:我有一个变量N, M, Q这样的问题:1 ≤ N, M, Q < 10^5

我正在这个错误是我声明一个2D整数尺寸的阵列10000 x 10000C++,并与挣扎SIGABRT在Codechef误差近2天。

现在,如果我们计算:

整数的典型大小:4个字节

数组中的单元数:10000 x 10000

总大小(以字节为单位):400000000字节= 4 * 10 ^ 8≈400 MB

您可以针对此类问题的解决方案在您的PC上运行(并非总是如此),因为它可以承受这种大小。

但是,编码站点(在线裁判)上的资源仅限于几个KB。

因此,该SIGABRT错误和其他此类错误。

结论:

在这样的问题中,我们不应该声明一个数组或向量或任何其他大小的DS,但我们的任务是使算法高效,使其在没有它们(DS)或内存较少的情况下也可以工作。

PS:可能有其他原因导致此错误。以上就是其中之一。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.