kill 和signal
命令解释
通常我会使用kill -9 pid强制杀死进程或者kill -15 pid温和的杀死进程,但我其实一直都不知道他们的具体含义。
kill --help 输出的汉化如下
root@ubuntu:~# kill --help
kill: kill [-s 信号名称 | -n 信号编号 | -信号名称] pid | jobspec ... or kill -l [信号名称]
向作业发送信号。
向由 PID 或 JOBSPEC 标识的进程发送名为 SIGSPEC 或 SIGNUM 的信号。如果没有 SIGSPEC 或 SIGNUM,则默认发送 SIGTERM。
选项:
-s 信号 信号是信号名称
-n 信号 信号是信号编号
-l 列出信号名称;如果 `-l` 后跟有参数,它们被认为是信号编号,将列出对应的名称
-L 与 -l 同义
Kill 是一个 shell 内建命令,有两个原因:它允许使用作业 ID 而不是进程 ID,如果你的进程创建限制已达到,它仍然可以杀死进程。
退出状态:
如果给出了无效选项或者发生错误,则返回非成功状态。
首先。看不懂JOBSPEC这是个什么东西,直接忽略,查看前半部分的命令模版kill [-s sigspec | -n signum | -sigspec] pid |
(这里如何看不懂命令模版的参数替换,建议先去了解一下,和模版字符串差不多的东西)
其次,调用kill -l的返回信息只是个列表,并没有说什么是信号到底个什么鬼,于是进一步查看man kill
--signal <信号>
指定要发送的信号。信号可以通过名称或编号指定。信号的行为在 signal(7) 手册页中有所解释。
于是,对于man命令也有了新的印象,man --help,,,,,, 进入查看 man 7 signal
信号处置
每个信号都有一个当前的处置方式,它决定了进程在接收到信号时的行为。
下面表格中的“动作”列指定了每个信号的默认处置方式,具体如下:
Term 默认动作是终止进程。
Ign 默认动作是忽略信号。
Core 默认动作是终止进程并转储核心文件(参见 core(5))。
Stop 默认动作是停止进程。
Cont 默认动作是如果进程当前处于停止状态,则继续进程。
进程可以使用 sigaction(2) 或 signal(2) 改变信号的处置方式。(后者在设置信号处理程序时较少可移植;详见 signal(2)。)使用这些系统调用,进程可以选择在信号送达时执行以下行为之一:执行默认动作;忽略信号;或者用信号处理程序捕获信号,即当信号送达时自动调用的程序员定义的函数。
默认情况下,信号处理程序在正常进程栈上被调用。也可以安排信号处理程序使用备用栈;有关如何执行此操作以及它在何时可能有用的讨论,请参见 sigaltstack(2)。
信号处置是每个进程的属性:在多线程应用程序中,特定信号的处置方式对所有线程都是相同的。
通过 fork(2) 创建的子进程继承了其父进程的信号处置方式的副本。在 execve(2) 期间,处理信号的处置方式被重置为默认值;忽略信号的处置方式保持不变。
以上可以说明一部分信号的作用,至于日常使用中信号的作用,我想估计还是只会用 SIGTERM、 SIGKILL
SIGKILL : 向指定进程、指定进程组的所有成员或系统上的所有进程发送信号。
SIGTERM : 默认操作是终止该进程。
值得注意的是kill的写法问题,例如SIGTERM信号可以有以下几种写法
kill <pid> # 默认信号就是SIGTERM
kill -15 <pid>
kill -TERM <pid>
kill -SIGTERM <pid>
kill -s TERM <pid>
SIGTERM和SIGKILL的区别
优雅关闭: 父进程收到关闭通知(程序自带的关闭命令,或者是 SIGTERM) ,总之程序可以得到一个明确的关闭指令,然后走由程序实现一套父进程和子进程的关闭流程,他们会保证程序信息的合理保存。
SIGTERM信号: 进程会收到指令,至于是否优雅关闭,则由程序的实现决定
SIGKILL信号 : 强制立即退出,进程不会受到关闭指令,也就不会执行程序内部的逻辑,从根本上就不可能优雅关闭
ps: 信号本身不会扩散,例如给父进程发送SIGKILL,子进程同样收到SIGKILL,必须在程序中明确指定他们的集合(手动列出进程id,或者进程组id)