Linux signal
是 Unix-like
系统进程间通信(IPC)
的一种方式,该通信是异步的,通过软中断完成(操作系统会将目标进程正常执行流程暂停,然后处理信号,如果进程注册了相应的信号处理函数(signal handler)
,那么就会调用对应的handler函数,否则会执行默认的信号处理函数)。
信号量
Linux 中可以使用 kill -l
可以查看 Signal 信号量,不同系统可能会有不同(扩展:kill 关闭进程命令):
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
- 1 SIGHUP:挂断信号,通常是在终端的控制进程结束时,通知同一
Session
内的各个进程,之后它们与终端不再关联
- 2 SIGINT:程序终止(interrupt)信号,通常是按下
Ctrl+C
时发生,通知进程终止
- 3 SIGQUIT:和SIGINT类似,通常是按下
Ctrl+
时发生,进程收到 SIGQUIT
退出时会产生 coredump文件
- 4 SIGILL:illeage信号,程序执行了非法指令,退出时会产生
coredump文件
- 5 SIGTRAP:断点指令或其它陷阱(
trap
)指令产生,常用于 debug
- 6 SIGABRT:调用abort函数生成的信号,退出时会产生
coredump文件
- 7 SIGBUS:非法地址,内存地址出错
- 8 SIGFPE:
FPE(floating-point exception)
浮点异常,发生致命的算术运算错误时,退出时会产生 coredump文件
- 9 SIGKILL:立即结束程序的运行,该信号不能被阻塞、处理和忽略。常用
kill -9
就是执行的该信号量
- 10 SIGUSR1:预留用户使用的信号量
- 11 SIGSEGV:尝试访问/写数据到未分配给自己的内存,退出时会产生
coredump文件
- 12 SIGUSR2:预留用户使用的信号量
- 13 SIGPIPE:管道破裂信号,通常发生在进程间通信,比如采用
FIFO(first in first out)
通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号
- 14 SIGALRM:时钟信号,alarm函数使用该信号
- 15 SIGTERM:程序结束(
terminate
)信号,与SIGKILL
不同的是该信号可以被阻塞和处理,kill
缺省产生该信号,来要求程序自己退出
- 17 SIGCHLD:子进程(child)退出时,父进程会收到该信号
- 18 SIGCONT:让停止(stopped)的进程继续执行,该信号不能被阻塞
- 19 SIGSTOP:暂停(stopped)进程的执行,该信号不能被阻塞,处理或忽略
- 20 SIGTSTP:停止进程的运行,该信号可以被处理和忽略,发生在用户
Ctrl+Z
时发生这个信号
- 21 SIGTTIN:后台进程要从用户终端读数据时
- 22 SIGTTOU:类似于SIGTTIN, 写终端或修改终端模式时收到
- 23 SIGURG:urgent紧急的,紧急数据或out-of-band数据到达socket时产生
- 24 SIGXCPU:超过CPU时间资源限制,可由
getrlimit/setrlimit
来读取/改变
- 25 SIGXFSZ:进程企图扩大文件以至于超过文件大小资源限制时产生
- 26 SIGVTALRM:虚拟时钟信号,类似于SIGALRM,计算的是该进程占用的CPU时间
- 27 SIGPROF:类似于SIGALRM/SIGVTALRM,但包括该进程用的CPU时间以及系统调用的时间
- 28 SIGWINCH:Windows Change,窗口大小改变时发出
- 29 SIGIO:文件描述符准备就绪,可以开始进行输入/输出操作
- 30 SIGPWR:Power failure
- 31 SIGSYS:非法的系统调用
场景
程序终止有两种可能:
- crash,程序抛出未被捕获的异常,在 Linux 中可以使用 gdb 调试
- 被自身或其他程序kill
因此,我们在写程序关闭服务时,需要考虑如何优雅(graceful
)地停止进程。所谓 优雅
就是要保证已有的请求都能处理完,需要持久化的状态、数据都保存成功,然后再结束进程。一般来说,可以通过发送信号
或者通过哨兵
(sentinel
)来结束。
示例
$ cat forever.py
#!/usr/bin/env python
import time
if __name__ == '__main__':
while 1:
time.sleep(0.5)
$ python3 forever.py &
[1] 9845
SIGKILL信号量
$ kill -9 9845
$ strace -t -e trace=all -p 9845
strace: Process 9845 attached
00:01:46 select(0, NULL, NULL, NULL, {tv_sec=0, tv_usec=279201}) = 0 (Timeout)
00:02:14 +++ killed by SIGKILL +++
[1]+ Killed python3 forever.py
参考:strace使用
SIGUSR1信号量
$ kill -10 9845
$ strace -t -e trace=all -p 9845
strace: Process 9845 attached
00:08:47 select(0, NULL, NULL, NULL, {tv_sec=0, tv_usec=500000}) = ? ERESTARTNOHAND (To be restarted if no handler)
00:08:47 --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=4126, si_uid=0} ---
00:08:47 +++ killed by SIGUSR1 +++
[1]+ User defined signal 1 python3 forever.py
SIGSEGV信号量
$ kill -11 9845
$ strace -t -e trace=all -p 9845
strace: Process 9845 attached
00:09:48 select(0, NULL, NULL, NULL, {tv_sec=0, tv_usec=500000}) = ? ERESTARTNOHAND (To be restarted if no handler)
00:09:49 --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_USER, si_pid=4126, si_uid=0} ---
00:09:49 +++ killed by SIGSEGV (core dumped) +++
[1]+ Segmentation fault (core dumped) python3 forever.py
参数说明:
- si_pid: sending process ID
- si_uid: Real user ID of sending process
可以使用 Auditd
监听服务是被谁 kill
的。