Linux 信号
信号是进程间通信的一种方式,同时它是一种异步的形式。先注册信号处理函数,当接收到信号的时候回调该函数进程处理。信号在系统中以数字的形式标识。每个信号对应着一个处理函数
信号表
来个python打印支持的信号
1 | import signal |
因为平时用的少,而ubuntu上的man 7 signal
的内容比较多,我只是在osx上看了一下singal的文档。它仅包含下列一些信号
序号 | 名称 | 默认动作 | 解释 |
---|---|---|---|
1 | SIGHUP | 终止进程 | 让程序挂起 |
2 | SIGINT | 终止进程 | 打断程序的执行 |
3 | SIGQUIT | dump | 程序 |
4 | SIGILL | dump | 非法指令 |
5 | SIGTRAP | dump | 陷阱 |
6 | SIGABRT | dump | 打断程序 |
7 | SIGEMT | dump | |
8 | SIGFPE | dump | 浮点数异常 |
9 | SIGKILL | 终止进程 | 杀死程序(不可修改) |
10 | SIGBUS | dump | 总线错误 |
11 | SIGSEGV | dump | 段错误 |
12 | SIGSYS | dump | 调用不存在的系统调用 |
13 | SIGPIPE | 终止进程 | 给一个没有读的管道写数据 |
14 | SIGALRM | 终止进程 | 实时计时器超时 |
15 | SIGTERM | 终止进程 | 软中断信号 |
16 | SIGURG | 已废弃 | |
17 | SIGSTOP | 停止进程 | 停止(不可修改) |
18 | SIGTSTP | 停止进程 | 从键盘生成停止信号 |
19 | SIGCONT | 废弃 | |
20 | SIGCHLD | 废弃 | |
21 | SIGTTIN | 停止进程 | 后台进程尝试读取终端时触发 |
22 | SIGTOU | 停止进程 | 后台进程尝试写入终端时触发 |
23 | SIGIO | 废弃 | |
24 | SIGXCPU | 终止进程 | 进程的cpu时间片到期(setrlimit) |
25 | SIGXFSZ | 终止进程 | 文件大小超过限制(setrlimit) |
26 | SIGVTLRM | 终止进程 | 虚拟时钟超时(setitimer) |
27 | SIGPROF | 终止进程 | profil时钟超时(setitimer) |
28 | SIGWINCH | 废弃 | |
29 | SIGINFO | 废弃 | |
30 | SIGUSR1 | 终止进程 | 用户自定义信号1 |
31 | SIGUSR2 | 终止进程 | 用户自定义信号2 |
信号的产生
信号可以产生自软件和硬件,硬件来说,我们最常用的Ctrl+C
来终止一个程序的执行,软件层面就是经常使用kill -9 pid
来强制终止一个程序。观察上表可以发现还有很多类似非法指令、陷入、段错误等等都是由操作系统触发反馈给应用程序。还有就是我们自己编写程序进行系统调用触发。比如python中常用的signal.alarm
信号的处理
观察信号表可以发现虽然有31个信号。但是默认动作只有终止进程、dump、停止进程三种。这算成了多对一的关系,多个信号对应一个处理结果。想想这其实是一种人为的约定。操作系统遇到一些异常的时候本可以直接卡擦掉进程。但是还是先知会你一声,万一程序作者觉得遇到这种情况还能再抢救一下那就不异常了。所以规定这么多信号id。还是为了方便编程的时候对信号进行分类处理。而且不仅仅约定俗成。后面还有两个自定义信号让你自己进行定义
信号处理基本有三种套路,默认、忽略、自定义。如果你觉得遇到异常你还可以再抢救一下那就自定义或者忽略。但是假如忽略掉所有的信号。管理人员可能就无法退出你的程序了。因此SIGKILL和SIGSTOP强制不允许被忽略或者自定义。所以kill -9
就是一个大杀器
但是很多人并不被推荐用kill -9
,而更多的人推荐用kill -15
。原因是强制虽然强。但是这样编程人员别无选择。有的程序在意外退出前需要做一些清理工作,比如删掉临时文件啥的。当然,渣水平的作者怎么可能会去自定义处理SIGTERM信号┑( ̄Д  ̄)┍
应用
比如tornado中可以使用信号机制记录响应时间超过阈值的栈帧
1 | import tornado.httpserver |
关于它的实现就比较简单了。先使用signal.signal(signal.SIGALRM,callback)
注册信号回调函数。在ioloop主循环中每次事件完成后将实时计时器归零signal.setitimer(signal.ITIMER_REAL, 0, 0)
后再重新开始计时。此处为什么不是用alarm而使用setitimer。因为前者只支持整数秒,后者支持浮点数