先简单说一下Signal是啥.(如果想直接使用可以不看)

Signal翻译过来中文就是信号- -
当然, 本身他就是Linux系统编程中非常重要的概念, 信号机制是进程之间传递消息的一种机制,

其全称为软中断信号
作用是通知进程发生了异步事件。进程之间可以调用系统来传递信号, 本身内核也可以发送信号给进程, 告诉该进程发生了某个事件.

注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。

接收信号的进程对不同的信号有三种处理方法

  1. 指定处理函数
  2. 忽略
  3. 根据系统默认值处理, 大部分信号的默认处理是终止进程

然后就是一大段类型了..
Linux系统有两大类信号

  1. POSIX标准的规则信号(regular signal 1-31编号)
  2. 实时信号(real-time signal 32-63)
规则信号

信号编号

名称

默认动作

说明

1

SIGHUP

终止

终止控制终端或进程

2

SIGINT

终止

由键盘引起的终端(Ctrl-c)

3

SIGQUIT

dump

控制终端发送给进程的信号, 键盘产生的退出(Ctrl-\),

4

GIGILL

dusmp

非法指令引起

5

SIGTRAP

dump

debug中断

6

SIGABRT/SIGIOT

dump

异常中止

7

SIGBUS/SIGEMT

dump

总线异常/EMT指令

8

SIGFPE

dump

浮点运算溢出

9

SIGKILL

终止

强制杀死进程(大招, 进程不可捕获)

10

SIGUSR1

终止

用户信号, 进程可自定义用途

11

SIGSEGV

dump

非法内存地址引起

12

SIGUSR2

终止

用户信号, 进程可自定义用途

13

SIGPIPE

终止

向某个没有读取的管道中写入数据

14

SIGALRM

终止

时钟中断(闹钟)

15

SIGTERM

终止

进程终止(进程可捕获)

16

SIGSTKFLT

终止

协处理器栈错误

17

SIGCHLD

忽略

子进程退出或中断

18

SIGCONT

继续

如进程停止状态则开始运行

19

SIGSTOP

停止

停止进程运行

20

SIGSTP

停止

键盘产生的停止

21

SIGTTIN

停止

后台进程请求输入

22

SIGTTOU

停止

后台进程请求输出

23

SIGURG

忽略

socket发送紧急情况

24

SIGXCPU

dump

CPU时间限制被打破

25

SIGXFSZ

dump

文件大小限制被打破

26

SIGVTALRM

终止

虚拟定时时钟

27

SIGPROF

终止

profile timer clock

28

SIGWINCH

忽略

窗口尺寸调整

29

SIGIO/SIGPOLL

终止

I/O可用

30

SIGPWR

终止

电源异常

31

SIGSYS/SYSUNUSED

dump

系统调用异常

注意: 由于不同系统中同一个数值对应的信号类型不一样, 所以最好使用信号名称.
信号的数值越小, 优先级越高.

OK, 现在来说说Python中的处理

先列几个常用的信号:

编号

信号名称

说明

2

SIGINT

当按下键盘(Ctrl-c)组合键时进程就会收到这个信号

15

SIGTERM

当用户输入 kill sigterm pid. 对应的进程就会收到这个信号. 这个信号进程是可以捕获并指定函数处理, 例如做一下程序清理等工作. 甚至忽视这个信号

9

SIGKILL

强制杀死进程, 这个信号进程无法忽视, 直接在系统层面把进程杀掉. 所以在Python中他的不能监听的

14

SIGALRM

闹钟信号

去码

先来一个例子

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    监听了SIGINT信号, 当程序在运行的时候同时按下键盘 Ctrl+c 就会输出
    收到信号 2 <frame object at 0x00000000021DD048>
    handler方法的两个参数分别是 信号编号, 程序帧
"""

import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal receive_times = 0 def handler(signalnum, handler): global receive_times print u"收到信号", signalnum, frame, receive_times receive_times += 1 if receive_times > 3: exit(0) # 自己走 def main(): signal.signal(signal.SIGINT, handler) # Ctrl-c # time.sleep(10) # SIGINT 信号同样能唤醒 time.sleep, 所以这里程序就会结束 while True: # 改成 while 效果会好点 pass if __name__ == '__main__': main() 
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    监听了SIGINT信号, 当程序在运行的时候同时按下键盘 Ctrl+c 就会输出
    收到信号 2 <frame object at 0x00000000021DD048>
    handler方法的两个参数分别是 信号编号, 程序帧
"""

import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal receive_times = 0 def handler(signalnum, handler): global receive_times print u"收到信号", signalnum, frame, receive_times receive_times += 1 if receive_times > 3: exit(0) # 自己走 def main(): signal.signal(signal.SIGINT, handler) # Ctrl-c # time.sleep(10) # SIGINT 信号同样能唤醒 time.sleep, 所以这里程序就会结束 while True: # 改成 while 效果会好点 pass if __name__ == '__main__': main()

再看看SIGTERM的效果

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    当我们运行该程序时因为 while True 所以会持续的运行. 
    这里监听的是 SIGTERM 信号, 所以当我们在终端输入 kill pid (linux kill
    默认是发送SIGTERM)时, 
    程序就会输出: 收到信号 15 <frame object at 0x7ff695738050> 0
    当超过3次时就强制把自己杀死.
    所以 SIGTERM 很适合用来做一些清理的工作
"""

import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal receive_times = 0 def handler(signalnum, frame): global receive_times print u"收到信号", signalnum, frame, receive_times receive_times += 1 if receive_times > 3: exit(0) # 自己走 def main(): print "pid:", os.getpid() signal.signal(signal.SIGTERM, handler) while True: pass if __name__ == '__main__': main() 
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    当我们运行该程序时因为 while True 所以会持续的运行. 
    这里监听的是 SIGTERM 信号, 所以当我们在终端输入 kill pid (linux kill
    默认是发送SIGTERM)时, 
    程序就会输出: 收到信号 15 <frame object at 0x7ff695738050> 0
    当超过3次时就强制把自己杀死.
    所以 SIGTERM 很适合用来做一些清理的工作
"""

import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal receive_times = 0 def handler(signalnum, frame): global receive_times print u"收到信号", signalnum, frame, receive_times receive_times += 1 if receive_times > 3: exit(0) # 自己走 def main(): print "pid:", os.getpid() signal.signal(signal.SIGTERM, handler) while True: pass if __name__ == '__main__': main()

刚才我们说过SIGKILL不能被监听.

signal.signal(signal.SIGKILL, handler) 
# 这里系统会直接跑错 AttributeError: 'module' object has no attribute 'SIGKILL'

signal.signal(signal.SIGKILL, handler) 
# 这里系统会直接跑错 AttributeError: 'module' object has no attribute 'SIGKILL'
最后来一个实际运用的例子

在python2.x的版本, 线程有个bug, 在join的时候不能接收信号
详解见:https://bugs.python.org/issue1167930 所以如果我们运行以下代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    这里虽然我们监听了 SIGINT 信号, 但是当我们按下Ctrl-c时程序并没有任何输出. 还是要等线程运行完成程序才退出.
"""

import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal import threading receive_times = 0 def handler(signalnum, frame): global receive_times print u"收到信号", signalnum, frame, receive_times receive_times += 1 if receive_times > 3: # os.kill(os.getpid(), signal.SIGTERM) # 我疯起来连自己都杀 exit(0) def run(): print "thread %s run:"%(threading.currentThread().getName()) time.sleep(10) print "thread %s done"%(threading.currentThread().getName()) def main(): print "pid:", os.getpid() signal.signal(signal.SIGINT, handler) thread_list = [] for i in range(5): thread = threading.Thread(target = run) thread_list.append(thread) for thread in thread_list: thread.start() for thread in thread_list: thread.join() print "all thread done" if __name__ == '__main__': main() 
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    这里虽然我们监听了 SIGINT 信号, 但是当我们按下Ctrl-c时程序并没有任何输出. 还是要等线程运行完成程序才退出.
"""

import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal import threading receive_times = 0 def handler(signalnum, frame): global receive_times print u"收到信号", signalnum, frame, receive_times receive_times += 1 if receive_times > 3: # os.kill(os.getpid(), signal.SIGTERM) # 我疯起来连自己都杀 exit(0) def run(): print "thread %s run:"%(threading.currentThread().getName()) time.sleep(10) print "thread %s done"%(threading.currentThread().getName()) def main(): print "pid:", os.getpid() signal.signal(signal.SIGINT, handler) thread_list = [] for i in range(5): thread = threading.Thread(target = run) thread_list.append(thread) for thread in thread_list: thread.start() for thread in thread_list: thread.join() print "all thread done" if __name__ == '__main__': main()

然后我们来改一下

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    在这里我们放弃了线程的join() 方法, 然后用 while True 的方式来代替, 然后在主进程判断线程的存活状态. 这样既能持续的运行线程又能根据需求来随时中断
"""

import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal import threading is_run_thread = True def handler(signalnum, frame): print u"收到信号", signalnum, frame global is_run_thread is_run_thread = False # 停止运行线程 def run(): print "thread %s run:"%(threading.currentThread().getName()) while is_run_thread: # do something pass print "thread %s done"%(threading.currentThread().getName()) def main(): print "pid:", os.getpid() signal.signal(signal.SIGINT, handler) thread_list = [] for i in range(5): thread = threading.Thread(target = run) thread_list.append(thread) for thread in thread_list: thread.start() # 注意这里 while True: for thread in thread_list: if thread.isAlive(): break else: break # for thread in thread_list: # thread.join() print "all thread done" if __name__ == '__main__': main() 
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    在这里我们放弃了线程的join() 方法, 然后用 while True 的方式来代替, 然后在主进程判断线程的存活状态. 这样既能持续的运行线程又能根据需求来随时中断
"""

import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal import threading is_run_thread = True def handler(signalnum, frame): print u"收到信号", signalnum, frame global is_run_thread is_run_thread = False # 停止运行线程 def run(): print "thread %s run:"%(threading.currentThread().getName()) while is_run_thread: # do something pass print "thread %s done"%(threading.currentThread().getName()) def main(): print "pid:", os.getpid() signal.signal(signal.SIGINT, handler) thread_list = [] for i in range(5): thread = threading.Thread(target = run) thread_list.append(thread) for thread in thread_list: thread.start() # 注意这里 while True: for thread in thread_list: if thread.isAlive(): break else: break # for thread in thread_list: # thread.join() print "all thread done" if __name__ == '__main__': main()

注意, 在wnidows系统中只能调用 SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, or SIGTERM