本文主要介绍Linux信号系统和如何使用POSIX API来响应信号。本文中的示例适用于Linux系统和大部分POSIX兼容系统。\
Linux系统中的信号
\在下列情况下,我们的应用进程可能会收到系统信号:\
- 用户空间的其他进程调用了类似kill(2)函数 \
- 进程自身调用了类似about(3)函数 \
- 当子进程退出时,内核会向父进程发送SIGCHLD信号 \
- 当父进程退出时,所有子进程会收到SIGHUP信号 \
- 当用户通过键盘终端进程(ctrl+c)时,进程会收到SIGINT信号 \
- 当进程运行出现问题时,可能会收到SIGILL、SIGFPE、SIGSEGV等信号 \
- 当进程在调用mmap(2)的时候失败(可能是因为映射的文件被其他进程截短),会收到SIGBUS信号 \
- 当使用性能调优工具时,进程可能会收到SIGPROF。这一般是程序未能正确处理中断系统函数(如read(2))。 \
- 当使用write(2)或类似数据发送函数时,如果对方已经断开连接,进程会收到SIGPIPE信号。
如需了解所有系统信号,参见signal(7)手册。\
信号的默认行为
\每个信号都关联一个默认的行为,当进程没有捕获并处理信号时,进程会按照默认的行为处理信号。\
这些默认行为包括:\
- 结束进程。这是最通用默认行为,包括SIGTERM、SIGQUIT、SIGPIPE、SIGUSR1、SIGUSR2等信号。 \
- 结束并执行核心转储。包括SIGSEGV、SIGILL、SIGABRT等信号,这一般都是因为代码中存在错误。 \
- 一些信号默认会被忽略,例如SIGCHLD。 \
- 挂起进程。SIGSTOP信号会引起进程挂起,而SIGCOND能够将挂起的进程继续运行。该过程常见于在控制台使用ctrl+z组合键。
信号处理
\最传统的信号处理方式是使用signal(2)函数装载一个信号处理函数。但是这种方式已经被废弃,主要原因是在UNIX实现中,收到信号之后,会重置回默认的信号处理行为。同时,该行为是不跨平台的。因此,建议的信号处理方式是使用sigaction(2)函数。\
sigaction(2)函数的原型为:
int sigaction (int signum, const struct sigaction *act, struct sigaction *oldact);\
值得注意的是,sigaction(2)函数不直接接受信号处理函数,而需要使用struct sigaction
结构体,其定义为:
struct sigaction {\ void (*sa_handler)(int);\ void (*sa_sigaction)(int, siginfo_t *, void *);\ sigset_t sa_mask;\ int sa_flags;\ void (*sa_restorer)(void);\};\
其中一些关键字段:\
- sa_handler:信号处理函数的函数指针,其函数原型和signal(2)接受的信号处理函数相同。 \
- sa_sigaction:另一种信号处理函数指针,它能在处理信号时获取更多信号相关的信息。 \
- sa_mask:允许设置信号处理函数执行时需要阻塞的信号。 \
- sa_flags:修改信号处理函数执行时的默认行为,具体可选值请参照手册。
sigaction使用示例:
#include \u0026lt;stdio.h\u0026gt;\#include \u0026lt;unistd.h\u0026gt;\#include \u0026lt;signal.h\u0026gt;\#include \u0026lt;string.h\u0026gt;\\static void hdl (int sig, siginfo_t *siginfo, void *context)\{\ printf (\"Sending PID: %ld, UID: %ld\\