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, intime.sleep(0.2) KeyboardInterrupt