SIGINT如何与其他终止信号如SIGTERM、SIGQUIT和SIGKILL相关联?
SIGINT如何与其他终止信号如SIGTERM、SIGQUIT和SIGKILL相关联?
在POSIX系统上,终止信号通常按照以下顺序(根据许多MAN页面和POSIX规范):
- SIGTERM - 礼貌地请求进程终止。它应该优雅地终止,清理所有资源(文件,套接字,子进程等),删除临时文件等。
- SIGQUIT - 更强有力的请求。它应该优雅地终止,仍然清理需要清理的所有资源,但可能不会删除临时文件,可能在某些系统上将调试信息写入某个位置;在某些系统上,还会写入核心转储(无论信号是否被应用程序捕获)。
- SIGKILL - 最强有力的请求。甚至都没有要求进程执行任何操作,但系统将清理进程,无论它是否喜欢这样做。最有可能写入核心转储。
SIGINT
在这种情况下如何适应呢?当用户按下CRTL+C
时,CLI进程通常会被SIGINT
终止,但是后台进程也可以使用KILL
实用程序使用SIGINT
终止。我在规范或标头文件中看不到SIGINT
是否比SIGTERM
更强有力,或者SIGINT
和SIGTERM
之间是否有任何区别。
更新:
我迄今为止发现的关于终止信号的最好描述在GNU LibC文档中。它很好地解释了SIGTERM
和SIGQUIT
之间的区别。
它讲了关于SIGTERM
的内容:
这是一种礼貌地请求程序停止运行的常规方式。
它也提到了SIGQUIT
:
[...]当进程终止时,它会生成一个核心转储文件,就像程序错误信号一样。
你可以将其视为用户“检测到”的程序错误条件。[...]在处理SIGQUIT时,最好不要进行某些清理操作。
例如,如果程序创建临时文件,则应通过删除临时文件来处理其他终止请求。但最好不要在处理SIGQUIT时删除它们,以便用户可以与核心转储文件一起查看它们。
关于SIGHUP
也有足够的解释。SIGHUP并不是真正的终止信号,它只是意味着与用户的“连接”已丢失,因此应用程序不能期望用户继续阅读任何输出(例如stdout/stderr输出),也不再期望从用户那里收到任何输入。对于大多数应用程序来说,这意味着它们最好退出。理论上,当收到SIGHUP时,一个应用程序也可以决定进入守护进程模式,现在作为后台进程运行,将输出写入配置的日志文件。对于正在后台运行的大多数守护程序,SIGHUP通常意味着它们应该重新检查其配置文件,因此在编辑配置文件后向后台进程发送SIGHUP。
然而,这个页面没有关于SIGINT的有用解释,除了它是由CRTL+C发送的。有没有任何理由在处理SIGINT时与处理SIGTERM不同?如果是这样,处理方式会有何区别?
man 7 signal
这是Linux man-pages项目中方便的非规范手册,你经常需要查看以获取Linux信号信息。
版本3.22提到了有趣的事情,例如:
信号SIGKILL和SIGSTOP不能被捕获、阻止或忽略。
并且包含表格:
Signal Value Action Comment ---------------------------------------------------------------------- SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process SIGINT 2 Term Interrupt from keyboard SIGQUIT 3 Core Quit from keyboard SIGILL 4 Core Illegal Instruction SIGABRT 6 Core Abort signal from abort(3) SIGFPE 8 Core Floating point exception SIGKILL 9 Term Kill signal SIGSEGV 11 Core Invalid memory reference SIGPIPE 13 Term Broken pipe: write to pipe with no readers SIGALRM 14 Term Timer signal from alarm(2) SIGTERM 15 Term Termination signal SIGUSR1 30,10,16 Term User-defined signal 1 SIGUSR2 31,12,17 Term User-defined signal 2 SIGCHLD 20,17,18 Ign Child stopped or terminated SIGCONT 19,18,25 Cont Continue if stopped SIGSTOP 17,19,23 Stop Stop process SIGTSTP 18,20,24 Stop Stop typed at tty SIGTTIN 21,21,26 Stop tty input for background process SIGTTOU 22,22,27 Stop tty output for background process
它总结了具有不同Action
的信号,例如SIGQUIT和SIGQUIT的区别,因为SIGQUIT有Core
的操作,而SIGINT有Term
。
这些操作在同一文档中有记录:
The entries in the "Action" column of the tables below specify the default disposition for each signal, as follows: Term Default action is to terminate the process. Ign Default action is to ignore the signal. Core Default action is to terminate the process and dump core (see core(5)). Stop Default action is to stop the process. Cont Default action is to continue the process if it is currently stopped.
从内核的角度来看,我看不出SIGTERM和SIGINT之间的区别,因为它们都有Term
操作,并且都可以被捕获。看起来这只是一个“通用使用惯例的区别”:
- SIGINT是从终端执行CTRL-C时发生的
- SIGTERM是默认信号由
kill
发送
一些信号是ANSI C,其他则不是
一个相当大的区别是:
- SIGINT和SIGTERM是ANSI C,因此更具可移植性
- SIGQUIT和SIGKILL则没有
它们在C99草案N1256的“7.14信号处理”部分中有描述:
- SIGINT是交互式关注信号的接收
- SIGTERM是发送到程序的终止请求
这使SIGINT成为交互式Ctrl+C的一个好选择。
POSIX 7
POSIX 7文件使用signal.h
头文件来描述信号:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
这个页面还有一个表格,提到了一些我们在man 7 signal
中已经看到的内容:
Signal Default Action Description SIGABRT A Process abort signal. SIGALRM T Alarm clock. SIGBUS A Access to an undefined portion of a memory object. SIGCHLD I Child process terminated, stopped, SIGCONT C Continue executing, if stopped. SIGFPE A Erroneous arithmetic operation. SIGHUP T Hangup. SIGILL A Illegal instruction. SIGINT T Terminal interrupt signal. SIGKILL T Kill (cannot be caught or ignored). SIGPIPE T Write on a pipe with no one to read it. SIGQUIT A Terminal quit signal. SIGSEGV A Invalid memory reference. SIGSTOP S Stop executing (cannot be caught or ignored). SIGTERM T Termination signal. SIGTSTP S Terminal stop signal. SIGTTIN S Background process attempting read. SIGTTOU S Background process attempting write. SIGUSR1 T User-defined signal 1. SIGUSR2 T User-defined signal 2. SIGTRAP A Trace/breakpoint trap. SIGURG I High bandwidth data is available at a socket. SIGXCPU A CPU time limit exceeded. SIGXFSZ A File size limit exceeded.
BusyBox init
BusyBox 1.29.2默认的reboot
命令向进程发送SIGTERM信号,等待一秒,然后发送SIGKILL信号。这似乎是不同发行版之间的一种普遍惯例。
当你使用下面的命令关机BusyBox系统:
reboot
它向init进程发送信号。
然后,init信号处理程序最终调用:
static void run_shutdown_and_kill_processes(void) { /* Run everything to be run at "shutdown". This is done _prior_ * to killing everything, in case people wish to use scripts to * shut things down gracefully... */ run_actions(SHUTDOWN); message(L_CONSOLE | L_LOG, "The system is going down NOW!"); /* Send signals to every process _except_ pid 1 */ kill(-1, SIGTERM); message(L_CONSOLE, "Sent SIG%s to all processes", "TERM"); sync(); sleep(1); kill(-1, SIGKILL); message(L_CONSOLE, "Sent SIG%s to all processes", "KILL"); sync(); /*sleep(1); - callers take care about making a pause */ }
它在终端上输出:
The system is going down NOW! Sent SIGTERM to all processes Sent SIGKILL to all processes
这里有一个最小的具体例子。
内核发送的信号
- SIGKILL:
- OOM killer: Linux内存管理中RSS和VSZ是什么
SIGTERM
和SIGKILL
是通用的"终止此进程"请求。默认情况下,SIGTERM
和SIGKILL
都会导致进程终止。进程可以捕获SIGTERM
(例如,如果它想要自己进行清理),甚至可以完全忽略它;但是SIGKILL
无法被捕获或忽略。
SIGINT
和SIGQUIT
特别用于来自终端的请求:可以分配特定的输入字符来生成这些信号(取决于终端控制设置)。默认情况下,SIGINT
的操作方式与SIGTERM
的默认操作方式相同,而SIGQUIT
的不可更改操作方式也是进程终止,但是可能会发生其他实现定义的操作,例如生成核心转储。如果需要,进程可以捕获或忽略任何信号。
SIGHUP
,正如您所说,旨在指示终端连接已丢失,而不是作为终止信号。但是,再次说明,如果进程不捕获或忽略它,则SIGHUP
的默认操作是以与SIGTERM
相同的方式终止进程。
POSIX定义中有一张表格,其中列出了各种信号及其默认操作和目的;General Terminal Interface章节包括有关终端相关信号的更多信息。