Linux C 捕捉终止信号以优雅终止

Posted

技术标签:

【中文标题】Linux C 捕捉终止信号以优雅终止【英文标题】:Linux C catching kill signal for graceful termination 【发布时间】:2011-09-11 04:02:16 【问题描述】:

我有一个使用套接字、数据库连接等的进程。它基本上是一个在传感器数据和 Web 界面之间中继的服务器进程,因此确保应用程序(如果被终止)正常终止非常重要。

如何处理意外异常,例如段错误(至少用于调试)以及终止信号,以便我可以关闭任何连接并停止任何正在运行的线程,以便进程不会留下它正在使用的任何东西?

【问题讨论】:

切记继续运行,甚至清理,在出现分段错误后可能会很危险。 还请记住,您无法捕捉到终止信号。 不确定你想避免什么混乱。杀死线程、关闭文件和释放内存通常由操作系统非常有效地完成,因此在大多数情况下,简单的退出就可以解决问题。你有什么具体的担心吗? @gabe - 最好说你抓不到SIGKILL,以免让那些从未使用过shell命令kill以外的人感到困惑;) @Soren:我不知道 OP 的问题,但确保文件始终处于一致状态、数据库事务回滚等通常需要操作系统无法处理的清理只需退出您的进程。 【参考方案1】:

捕捉信号很难。你必须要小心。您的第一步是使用sigaction 为所需信号安装信号处理程序。

选择一组信号来响应并选择它们对您的流程意味着什么。比如SIGTERM退出、SIGHUP重启、SIGUSR1重新加载配置等。

不要尝试响应所有信号,也不要尝试在指示程序错误的信号之后“清理”。 SIGKILL 抓不到。 SIGSEGVSIGBUS 和其他类似的人不应该被抓住,除非你有很好的理由。如果你想调试,那么提高核心转储的 ulimit — 将调试器附加到核心映像比你或我编写的任何代码都有效得多。 (如果您确实尝试在SIGSEGV 或类似的东西之后进行清理,请意识到清理代码可能会导致额外的SIGSEGV 并且事情可能会很快变得糟糕。避免整个混乱并让SIGSEGV 终止您的程序.)

处理信号的方式很棘手。如果您的应用程序有一个主循环(例如,selectpoll),那么信号处理程序可以设置一个标志或将一个字节写入一个特殊管道以指示主循环退出。您也可以使用siglongjmp 跳出信号处理程序,但这很难做到正确,而且通常不是您想要的。

如果不了解您的应用程序的结构和功能,很难推荐一些东西。

还要记住,信号处理程序本身应该几乎什么都不做。从信号处理程序调用许多函数是不安全的。

【讨论】:

【参考方案2】:

您安装信号处理程序来捕获信号——但在 99% 的情况下,您只想退出并让 Linux 操作系统负责清理工作——它会愉快地关闭所有文件、套接字、空闲内存和关闭线程。

因此,除非您有什么特别想做的事情,例如在套接字上发送消息,否则您应该退出进程而不是尝试捕获信号。

【讨论】:

我想你就在这里......在我的情况下,应用程序的可靠性更重要,用信号做疯狂的事情可能会很糟糕。当发生段错误时,如果我得到核心转储,它会告诉您段错误发生在哪里吗? 调用堆栈告诉你发生了段错误,假设调用堆栈没有损坏——这个问题在这里:***.com/questions/105659/…谈论如何获取堆栈跟踪 很好的建议。此规则的一个例外是当您需要在程序关闭时(在嵌入式系统中)清理或更改硬件。 老问题,但我仍然想知道你所说的 99% 是什么意思? 我的意思是,除非您是一位经验丰富的程序员,并且确切地知道特定应用程序应该捕获信号的原因,否则您最好不要这样做。【参考方案3】:

我有时喜欢在 SIGSEGV 上获得回溯,捕捉部分是这样的:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void sig_handler(int);

int main() 
    signal(SIGSEGV, sig_handler);
    int *p = NULL;
    return *p;


void sig_handler(int sig) 
    switch (sig) 
    case SIGSEGV:
        fprintf(stderr, "give out a backtrace or something...\n");
        abort();
    default:
        fprintf(stderr, "wasn't expecting that!\n");
        abort();
    

您确实希望非常小心地处理这些事情,例如确保你不能触发另一个信号。

【讨论】:

使用ulimit 获取核心转储并以这种方式获取堆栈跟踪不是更容易吗? 不熟悉这种方法,您能详细说明一下吗?我只使用这些东西来调试顺便说一句,即我犯了一个错误并想立即查看信号来自哪里。 你真的不应该使用signal。多年来,一直建议不要这样做,而是使用sigaction。来自man signalsignal() 的行为因 Unix 版本而异,并且在历史上因 Linux 的不同版本而异。避免使用它:改用 sigaction(2)。。我没有反对……但它已经接近了。您不应该推荐任何人使用它。 ulimit 命令允许您使程序在SIGSEGV 上转储核心。核心文件是程序内存映像的副本,您可以将调试器附加到它并在内存中四处寻找(获取堆栈跟踪,检查变量),或者如果它不是您的程序,则将核心文件发送给开发人员。

以上是关于Linux C 捕捉终止信号以优雅终止的主要内容,如果未能解决你的问题,请参考以下文章

Linux 上的 Ctrl+C 等终止信号或中断

如何用给定的信号终止python程序? [复制]

电路中signal path怎么看

C memset - 优雅地添加一个空终止符

Kubernetes优雅终止的流程和配置方法(转载)

优雅地终止基于 Boost Asio 的 Windows 控制台应用程序