进程什么时候得到SIGABRT(信号6)?

在c++中,进程获得SIGABRT的场景是什么?这个信号总是来自进程内部还是可以从一个进程发送到另一个进程?

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

527355 次浏览

你可以使用kill(2)接口向任何进程发送任何信号:

kill -SIGABRT 30823

30823是我启动的dash进程,所以我可以很容易地找到我想要终止的进程。

$ /bin/dash
$ Aborted

Aborted输出显然是dash报告SIGABRT的方式。

它可以使用kill(2)直接发送给任何进程,或者进程可以通过assert(3)abort(3)raise(3)将信号发送给自己。

abort()向调用进程发送SIGABRT信号,这就是abort()的基本工作方式。

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

SIGABRT通常被libc和其他库用于在出现严重错误时中止程序。例如,glibc在检测到double-free或其他堆损坏时发送SIGABRT

此外,大多数assert实现在断言失败的情况下使用SIGABRT

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

它通常发生在内存分配有问题的时候。

它发生在我身上,当我的程序试图分配一个

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

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

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

在c++中还有另一个简单的原因。

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

例如,线程范围结束,但你忘记调用

thread::join();

thread::detach();

在我的例子中,这是由于在一个数组中输入的一个索引等于数组的长度。

string x[5];


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


cin>>x[i];


}

X[5]正在被访问,但不存在。

进程从自身获取SIGABRT的情况: Hrvoje提到了一个从ctor调用的被掩埋的纯虚拟生成一个中止,我为此重新创建了一个例子。 这里,当要构造d时,它首先调用它的基类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

无限制

:运行。/ 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

校验码:

灾害学0 x7feae3170c37

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

:)

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

很多时候在做cp的时候,约束条件是相当大的。

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

我犯的错误是我在C++中声明了一个大小为10000 x 10000的2D整数数组,并在Codechef中与SIGABRT错误斗争了近2天。

现在,如果我们计算:

整数的典型大小:4字节

不。数组中的单元格:10000 x 10000

总大小(字节):400000000字节= 4*10^8≈400mb

你对这些问题的解决方案将在你的PC上工作(不总是),因为它可以负担得起这个大小。

但编码网站(在线裁判)的资源有限,只有几kb。

因此,SIGABRT错误和其他类似的错误。

<强>结论:< / >强

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

PS:这个错误可能有其他原因;上面是其中一个。

正如“@sarnold”所指出的,任何进程都可以向任何其他进程发送信号,因此,一个进程可以向其他进程发送SIGABORT &在这种情况下,接收进程无法区分它的到来是因为它自己的内存调整等,还是其他人已经“unicastly”发送给它。

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

我只是想分享这一前景参考问题。

关于第一个问题:What are the scenarios where a process gets a SIGABRT in C++?

我可以想到两种c++程序自动中止的特殊情况——通过直接调用std::abort()std::terminate()来实现:

一:在处理异常时抛出异常。

try {
throw "abc";
}
catch (...) {
throw "def";  // abort here
}

二:试图在main()外部传播的未捕获异常。

int main(int argc, char** argv)
{
throw "abc";  // abort here
}

c++专家可能还能举出更多的特殊情况。

在这些参考页面上也有很多好的信息:

对于Android原生代码,以下是根据https://source.android.com/devices/tech/debug/native-crash调用abort的一些原因:

中止之所以有趣,是因为它们是有意为之的。中止有许多不同的方法(包括调用中止(3)维护(3)失败,使用android特定的致命日志类型之一),但都涉及调用中止。

错误munmap_chunk invalid pointer也会导致SIGABRT,在我的情况下,它很难调试,因为我根本没有使用指针。事实证明,它与std::sort()有关。

std::sort()需要一个比较函数来创建一个严格的弱排序!这意味着当a==b成立时,comparator(a, b)comparator(b, a)都必须返回false。在我的例子中,我在我的结构中定义了operator<,如下所示:

bool operator<(const MyStruct& o) const {
return value <= o.value; // Note the equality sign
}

这导致SIGABRT,因为函数没有创建严格的弱顺序。删除=解决了这个问题。