在C中捕获段错误

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

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

2)这有多便携?

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

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

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

那么,SIGSEGV是可以捕获的,这是POSIX,所以它在这个意义上是可移植的。

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

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

  #include  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); } 

信号处理程序中的安全操作非常有限。 调用任何未知的库函数是不安全的,这将排除例如free()printf() 。 最佳做法是设置变量并返回,但这对您没有多大帮助。 使用诸如write()类的系统调用也是安全的。

请注意,在这里给出的两个回溯示例中, backtrace_symbols_fd()函数将是安全的,因为它直接使用原始fd,但是对fprintf()的调用不正确,应该使用write()替换。

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

我建议你重新考虑一下你的投入策略:为什么不能对它进行消毒? 最重要的是大小检查,为此,C stdlib具有适当的function。 当然,您必须检查有关内容的有效输入。 是的,这可能会带来很多工作,但这是编写健壮程序的唯一方法。

编辑:我不是一个C专家,不知道甚至一个分段错误可以由信号处理程序处理。 不过,我认为由于上述原因,这不是正确的方法。

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

信号处理在unix机器上(相对)可移植(包括mac和linux)。 差异在于exception细节,它作为参数传递给信号处理例程。 Sorrty,但如果你想打印更合理的错误信息(比如故障发生在哪里和由于哪个地址),你可能需要一堆#ifdefs …

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

 #include  /* reached when a segv occurrs */ void SEGVFunction( SIGARGS ) { ... } ... main(...) { signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */ ... ... do your work ... } 

你的任务是:

  • 检查SIGARGS是什么(依赖于OS,所以使用ifdef)
  • 了解如何从sigArgs中的exception信息中提取fault-address和pc
  • 打印合理的信息
  • 出口

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

问候

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

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

您可以使用它来捕获您的段错误并进行清理,但要注意:您不应该在信号处理程序中执行太多操作,尤其是涉及调用malloc()之类的操作。 有很多不是信号安全的电话,如果你从malloc内部调用malloc,你最终可能会在脚下开枪。