Python标准库的signal模块提供了信号处理的功能。
本文是在Python 2.7.5下测试通过的。Python3X对signal模块进行了增强,本文不做讨论。
signal.signal()函数用于给信号自定义处理函数。当进程收到信号时,就会执行相应的信号处理函数。
下面看一个例子:
[root@iZj6chejzrsqpclb7miryaZ ~]# cat t.py
# coding: utf8
import time
import os
import threading
import signal
IS_QUIT = False
# 信号处理函数的第一个参数是信号,第二个参数是当前的桢对象
def on_quit(signal_number, frame_object):
print "thread: %s received signal: %d" % (
threading.current_thread(), signal_number)
global IS_QUIT
IS_QUIT = True
if __name__ == "__main__":
signal.signal(signal.SIGUSR1, on_quit)
print "pid is: %d" % os.getpid()
while not IS_QUIT:
time.sleep(0.1)
[root@iZj6chejzrsqpclb7miryaZ ~]# python t.py
pid is: 7898
打开另外一个窗口,执行:
kill -USR1 7898
回到第一个窗口,会看到,类似下面的信息:
thread: <_MainThread(MainThread, started 140363276531520)> received signal: 10
并且程序从循环中退出了。
kill -l列出所有的信号kill -<宏名/数字> <pid>命令发送信号;在Python中,可以通过os.kill(<pid>, <sig>)函数发送信号1,singal.pause()[TOC]
该函数会导致进程休眠,一直到收到一个信号。同时,相应的信号处理函数也会被执行。(Windows不支持)
[root@iZj6chejzrsqpclb7miryaZ ~]# cat t.py import signal import os print "pid is: %d" % os.getpid() signal.signal(signal.SIGINT, lambda *a, **kw: None) signal.pause() [root@iZj6chejzrsqpclb7miryaZ ~]# python t.py pid is: 7944 ^C[root@iZj6chejzrsqpclb7miryaZ ~]#
2,signal.set_wakeup_fd(fd)[TOC]
将wakeup描述符设置为fd。当收到信号时,'\0'这个字节会被写到fd。该函数通常用于唤醒poll或select调用。同时,需要信号被信号处理函数处理。
该函数会返回之前设置的wakeup描述符,如果原来没有设置过wakeup描述符,那么返回-1。如果将fd设置为-1,那么会关闭wakeup功能;否则,fd必须是非阻塞的。使用signal.set_wakeup_fd()函数的库,在再次调用poll或select之前,应该读尽fd的缓冲区。
在非主线程上调用该函数,会引发ValueError异常。
[root@iZj6chejzrsqpclb7miryaZ ~]# cat t.py
# coding: utf8
import signal
import os
import struct
import socket
print "pid is: %d" % os.getpid()
class Waker:
def __init__(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定0端口 - 操作系统会随机分配一个空闲端口
sock.bind(("127.0.0.1", 0))
host, port = sock.getsockname()
sock.listen(1)
self.writer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.writer.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.writer.connect((host, port))
self.writer.setblocking(False)
self.reader, _ = sock.accept()
self.reader.setblocking(False)
sock.close()
def consume(self):
try:
while True:
data = self.reader.recv(1024)
if not data:
break
print repr(data)
except (IOError, socket.error) as ex:
pass
def get_wakeup_fd(self):
return self.writer.fileno()
waker = Waker()
def on_sig_int(sn, fo):
print "on_sig_int"
waker.consume()
signal.signal(signal.SIGINT, on_sig_int)
signal.set_wakeup_fd(waker.get_wakeup_fd())
while True:
import time
time.sleep(0.2)
[root@iZj6chejzrsqpclb7miryaZ ~]# python t.py
pid is: 9148
^Con_sig_int
'\x00'
^Z
[1]+ 已停止 python t.py
[root@iZj6chejzrsqpclb7miryaZ ~]# kill -9 %1
[1]+ 已停止 python t.py
3,定时器之:signal.alarm(time)[TOC]
如果time非0,那么该函数会在time秒之后,向进程发送SIGALRM信号,之前设置的alarm会被取消;如果time为0,则只是简单的取消之前设置的alarm。该函数的返回值表示之前设置的alarm距离投递还有多少秒。time必须是整数。
[root@iZj6chejzrsqpclb7miryaZ ~]# cat t.py
import signal
import time
def on_sig_alarm(sn, fo):
print "receive alarm after %s second(s)" % (time.time() - start_time)
signal.signal(signal.SIGALRM, on_sig_alarm)
signal.alarm(3)
time.sleep(1)
print signal.alarm(1)
start_time = time.time()
time.sleep(10)
[root@iZj6chejzrsqpclb7miryaZ ~]# python t.py
2
receive alarm after 0.999886989594 second(s)
4,定时器之:signal.setitimer(which, seconds, interval)[TOC]
which用来指定定时器的类型:
signal.ITIMER_REAL:真实时间定时器,和alarm相同,会发送SIGALRM信号signal.ITIMER_VIRTUAL:用户态时间定时器,会发送SIGVTALRM信号signal.ITIMER_PROF:用户态+内核态时间定时器,会发送SIGPROF信号seconds:表示在seconds秒后发送第一个信号
interval:表示之后每interval秒发送一个信号
当seconds等于0时,会取消之前设置的which类型的定时器。
该函数返回,之前设置的which类型的定时器,距离投递还有多少秒 以及 该类型的定时器的interval。
[root@iZj6chejzrsqpclb7miryaZ ~]# cat t.py
import os
import signal
def on_sig_alrm(sn, fo):
print "on_sig_alrm"
print "do something here"
def handle_signal():
signal.signal(signal.SIGALRM, on_sig_alrm)
signal.setitimer(signal.ITIMER_REAL, 1, 1)
if __name__ == "__main__":
print "pid: %d" % os.getpid()
handle_signal()
import time
while True:
time.sleep(0.2)
[root@iZj6chejzrsqpclb7miryaZ ~]# python t.py
pid: 9342
on_sig_alrm
do something here
on_sig_alrm
do something here
^CTraceback (most recent call last):
File "t.py", line 18, in
time.sleep(0.2)
KeyboardInterrupt