【问题标题】:When does a process get SIGABRT (signal 6)?进程何时获得 SIGABRT(信号 6)?
【发布时间】:2021-06-17 03:20:30
【问题描述】:

在 C++ 中进程获得 SIGABRT 的场景有哪些?这个信号总是来自进程内部还是可以从一个进程发送到另一个进程?

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

【问题讨论】:

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

标签: c++ sigabrt


【解决方案1】:

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

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

【讨论】:

  • 对我来说,在大多数情况下,SIGABRT 是由 libc 发送的,试图在未初始化/损坏的指针上调用 free()
  • 如果我在代码中的某个地方隐藏了构造函数中的纯虚函数调用,那是否也会以 SIGABRT 信号结束?我在问,因为我看到一个错误,说明我有一个纯虚拟呼叫,下一行给我一条 SIGABRT 消息,应用程序崩溃或被操作系统关闭。谢谢。
  • 在 MacOS 上,我们得到了 SIGABRT 来打开大约 1000 个文件句柄而不关闭它们。我们的测试不是模拟,而是使用更通用的读取器类型抽象文件,它没有Close() 方法,所以它被遗忘了。虽然有很好的报道。 :rolleyes:
【解决方案2】:

SIGABRT 通常被 libc 和其他库用于在出现严重错误时中止程序。例如,glibc 发送一个SIGABRT 以防检测到双释放或其他堆损坏。

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

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

【讨论】:

    【解决方案3】:

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

    kill -SIGABRT 30823

    30823 是我启动的dash 进程,因此我可以轻松找到我想杀死的进程。

    $ /bin/dash
    $ Aborted
    

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

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

    【讨论】:

      【解决方案4】:

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

      当我的程序试图分配一个 具有负大小的数组。

      【讨论】:

        【解决方案5】:

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

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

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

        thread::join();
        

        或

        thread::detach();
        

        【讨论】:

          【解决方案6】:

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

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

          Catching libc error messages, redirecting from /dev/tty

          【讨论】:

            【解决方案7】:

            进程从自身获取 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
            

            校验码:

            disas 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

            :)

            【讨论】:

              【解决方案8】:

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

              string x[5];
              
              for(int i=1; i<=5; i++){
              
                  cin>>x[i];
              
              }
              

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

              【讨论】:

                【解决方案9】:

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

                很多时候做cp,约束都很大。

                例如:我有一个关于变量N, M, Q的问题,1 ≤ N, M, Q &lt; 10^5强>.

                我犯的错误是我在 C++ 中声明了一个大小为 10000 x 10000 的二维整数数组,并在 Codechef 中为 SIGABRT 错误苦苦挣扎了将近 2 天。

                现在,如果我们计算:

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

                没有。我们数组中的单元格数:10000 x 10000

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

                您对此类问题的解决方案将适用于您的 PC(并非总是),因为它可以承受这种尺寸。

                但编码站点(在线评委)的资源仅限于几 KB。

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

                结论:

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

                PS :这个错误可能还有其他原因;以上就是其中之一。

                【讨论】:

                  【解决方案10】:

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

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

                  我只是想根据所提出的问题分享这个前景。

                  【讨论】:

                    【解决方案11】:

                    关于第一个问题: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++ 专家可能会说出更多特殊情况。

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

                    【讨论】:

                      【解决方案12】:

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

                      中止很有趣,因为它们是经过深思熟虑的。有许多不同的中止方法(包括调用abort(3)、失败assert(3)、使用Android 特定的致命日志记录类型之一),但都涉及调用中止。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2016-01-11
                        • 2016-01-05
                        • 2014-04-29
                        • 2014-12-21
                        • 2019-01-27
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多