SIGINT 和linux中的其他终止信号
Last updated
Last updated
在linux系统中,进程可以接收各种各样的信号,例如:SIGINT、SIGKILL。每一个信号在不同的情况下被发送并且每一个都会触发不同的行为。
在这篇文章中,我们将要讨论SIGINT、SIGTERM、SIGQUIT和SIGKILL。我们将要查看他们之间的不同之处。
信号是进程之间交流的方式。当进程接收一个信号时,它会中断正在执行的程序并开始一个信号处理器的执行。
进程有怎样的行为通常取决于被接收的信号的类型。处理信号之后,进程可能继续或者中断它的正常运行。
例如,当一个进程尝试执行除以0的算法时,会接收到来自linux内核发送的SIGFPE信号。
我们也可以通过kill
命令发送信号。在后台运行一个简单脚本并停止它。
现在,我们用SIGCONT信号执行这个脚本。
或者,我们可以在终端通过组合键发送信号。例如,Ctrl+C 发送 SIGINT, Ctrl+S 发送 SIGSTOP, Ctrl+Q 发送 SIGCONT。
每一个信号有一个默认的行为,但是进程可以重写默认行为修改其默认处理方式活着忽视它。可是,一些信号不可以被忽视或者被修改处理方式,其默认行为是一直被执行的。
我们可以在bash中用trap
命令处理信号。例如,我们可以在脚本中添加trap date SIGINT
,那么当SIGINT命令被接收的时候,程序就会输出日期。
SIGINT是当我们按压Ctrl+C时发送的信号。它的默认操作是终止进程。可是,一些程序会重载这个默认操作并用不同的方式来处理。
一个常见的例子就是bash解释器。当我们按压Ctrl+C时,程序不会退出,取而代之,它输出一行新的并且空的提示符。另一个例子就是当我们用gdb调试程序,我们通过Ctrl+C发送SIGINT信号去停止正在执行的gdb的解释器并返回它。
我们可以认为SIGINT被用户发送的中断请求。它怎样处理通常依赖于这个程序和场景。
用trap命令修改handle_sigint.sh脚本去处理SIGINT并输出当前日期。
我们通过读输入去等待用户交互。现在,运行脚本并按压Ctrl+C:
我们可以看到这个脚本不存在。我们现在可以通过输入一些内容去中断脚本。
如果我们想用信号去中断它,我们不可以用SIGINT去处理这个脚本。我们可以用SIGTERM、SIGQUIT、或者SIGKILL来代替。
SIGTERM和SIGQUIT信号是中断进程的意思。在这个情况下,我们特别要求完成它。SIGTERM是我们用kill命令时的默认信号。
这两个信号的默认行为都是去中断进程。可是,SIGQUIT在退出之前也会产生一个核心转储。
当我们发送SIGTERM信号,进程在退出之前有时执行一个清理程序。
我们也可以处理SIGTERM去要求在退出之前做确认。修改handle_sigterm.sh脚本,设置只有当用户发送两次信号后才中断程序。
现在,在后台执行$ ./handle_sigterm.sh &
.
,然后运行
$ kill <PID>
两次。
正如我们所看到的,这个脚本在接收到两次SIGTERM信号后退出了。
当一个进程接收到SIGKILL时,它会被中断。这是一个不可被忽视和修改行为的特殊信号。
我们用这个信号强制性的终止进程。当程序不可以执行任何清理程序的时候我们应该小心。
适用SIGKILL一个常见的方式是首先发送SIGTERM命令。我们给进程一些时间去退出。我们也可以多次发送SIGTERM。如果这个进程不能自己去结束,我们再通过SIGKILL去退出它。
重写上一个例子,试着去处理SIGKILL,要求在退出的时候进行确认。
现在,在终端运行脚本,用 $ kill -SIGKILL <pid>
发送SIGKILL信号。
我们发现,程序没有任何要求重新发送信号的请求,立刻终止。
现在关于信号,我们了解了更多,也可以明白他们相互之间的联系。
SIGINT、SIGTERM、SIGQUIT、SIGKILL的默认行为是中断程序。可是,SIGTERM、SIGQUIT、SIGKILL是作为中断进程的信号被定义的,但是SIGINT是作为来自用户的中断请求被定义的。
其中,如果我们发送SIGINT(或者按Ctrl+C),根据这个程序和情况,可能会有不同的行为。因此,我们不能完全的依靠SIGINT去结束进程。
当SIGINT意图作为用户发送的信号时,通常进程之间的交互是通过其他信号进行的。例如,一个父进程通常发送SIGTERM去中断它的子进程,即使SIGINT有同样的作用。
SIGQUIT在被调用的时候生成一个核心转储。
现在,我们可以明白,在SIGKILL之前,我们应该选择SIGTERM去结束程序。当一个进程有机会优雅的结束运行时,SIGTERM是更好的方式。
当一个进程可以重载SIGINT、SIGTERM、SIGQUIT的默认行为时,他们其中的任何一个可能都无法结束程序。还有,如果程序是挂起的,它可能无法响应其中的任何信号。在这种情况下,我们可以用SIGKILL作为最后的程序去终止这个进程。
在这篇文章中,我们学到了信号以及SIGINT、SIGTERM、SIGQUIT、SIGKILL之间的不同。另外,我们也简单学习了如何在bash中处理信号。
我们学到,当SIGINT有一个不同的意味时可能无法杀死进程。另一方面,SIGKILL将一直可以终止进程。
我们也了解到了SIGQUIT默认生成一个核心转储并且SIGTERM是杀死进程的更好方法。