12

我有一个程序有时会从指针算术中出错。我知道会发生这种情况,但我不能轻易地提前检查它是否存在段错误 - 我可以“预扫描”输入数据以查看它是否会导致段错误(这可能无法确定),或者我可以修改它以不使用指针算术,这需要大量的工作,或者我可以尝试捕获段错误。所以我的问题:

1)如何在 C 中捕获段错误?我知道操作系统中的某些东西会导致段错误,但是如果 C 程序在段错误中比仅仅更优雅地死掉,那么它可以做什么Segmentation fault呢?

2)这有多便携?

我想这是一个非常不可移植的行为,所以如果您发布任何代码来捕获段错误,请告诉我它的工作原理。我在 Mac OS X 上,但我希望我的程序可以在尽可能多的平台上运行,我想看看我的选择是什么。

别担心 - 基本上我想做的就是打印一个更用户友好的错误消息并释放一些malloc()ed 内存,然后死掉。我不打算只是忽略我得到的所有段错误并继续前进。

4

8 回答 8

23

好吧,SIGSEGV 是可捕获的,这是 POSIX,因此在这个意义上它是可移植的。

Bu 我担心您似乎想要处理段错误而不是修复导致段错误的问题。如果我必须选择是操作系统有问题还是我自己的代码有问题,我知道我会选择哪一个。我建议你找到那个错误,修复它,然后编写一个测试用例以确保它不会再咬你。

于 2009-02-16T18:45:22.957 回答
17

您可以使用函数signal为信号安装新的信号处理程序:

   #include <signal.h>
   void (*signal(int signum, void (*sighandler)(int)))(int);

类似于以下代码:

signal(SIGINT , clean_exit_on_sig);
signal(SIGABRT , clean_exit_on_sig);
signal(SIGILL , clean_exit_on_sig);
signal(SIGFPE , clean_exit_on_sig);
signal(SIGSEGV, clean_exit_on_sig); // <-- this one is for segmentation fault
signal(SIGTERM , clean_exit_on_sig);

void 
clean_exit_on_sig(int sig_num)
{
        printf ("\n Signal %d received",sig_num);
}
于 2009-02-16T18:49:59.060 回答
10

您必须定义一个信号处理程序。这是在 Unix 系统上使用函数完成的sigaction。我在 Fedora 64 位和 32 位以及 Sun Solaris 上使用相同的代码完成了这项工作。

于 2009-02-16T18:44:30.400 回答
5

The safe actions in a signal handler are very limited. It's unsafe to call any library function not known to be re-entrant, which will exclude, for example, free() and printf(). Best practice is to set a variable and return, but this doesn't help you very much. It's also safe to use system calls such as write().

Note that in the two backtrace examples given here, the backtrace_symbols_fd() function will be safe because it uses the raw fd directly, but the call to fprintf() is incorrect, and should be replaced by a use of write().

于 2009-02-16T19:17:51.850 回答
1

你需要提供一个 SIGSEGV 处理程序,这个看起来相当不错。

于 2009-02-16T18:51:44.033 回答
1

这里有一个如何使用 glibc 的 backtrace() 捕获 SIGSEGV 并打印堆栈跟踪的示例:

当我的 C++ 应用程序崩溃时如何生成堆栈跟踪

您可以使用它来捕获您的段错误并进行清理,但请注意:您不应该在信号处理程序中做太多的事情,尤其是涉及像 malloc() 这样的调用的事情。有很多调用不是信号安全的,如果你从 malloc 中调用 malloc,你最终可能会自取其辱。

于 2009-02-16T18:56:45.473 回答
1

信号处理(相对)可跨 unix 机器(包括 mac 和 linux)移植。最大的区别在于异常细节,它作为参数传递给信号处理例程。抱歉,但如果您想打印更合理的错误消息(例如故障发生的位置和原因),您可能需要一堆#ifdefs ...

好的,这是一个代码片段供您开始:

#include <signal.h>

/* reached when a segv occurrs */
void
SEGVFunction( SIGARGS )
{
     ...
}

...
main(...) {
    signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */
    ...
    ... do your work ...
}

你的任务是:

  • 检查 SIGARGS 是什么(取决于操作系统,所以使用 ifdef)
  • 查看如何从 sigArgs 中的异常信息中提取故障地址和 pc
  • 打印合理的信息
  • 出口

理论上,您甚至可以在信号处理程序中修补 pc(在错误指令之后),然后继续。但是,典型的信号处理程序要么 exit() 要么将 longjmp() 返回到 main 中的保存位置。

问候

于 2009-02-16T19:00:07.087 回答
0

我认为您正在尝试解决一个不存在的问题。至少你的工作是错误的。您将无法捕获分段错误,因为此错误/异常是由操作系统引发的(它是由您的程序引起的,操作系统只是捕获它)。

我建议您重新考虑有关输入的策略:为什么无法对其进行消毒?最重要的是大小检查,为此 C 标准库具有适当的功能。然后,您当然必须检查有关内容的有效输入。是的,这可能会导致大量工作,但这是编写健壮程序的唯一方法。

编辑:我不是 C 专家,不知道即使是分段错误也可以由信号处理程序处理。尽管如此,由于上述原因,我认为这不是正确的方法。

于 2009-02-16T18:48:44.837 回答