Python信号处理

栏目: Python · 发布时间: 6年前

内容简介:Python信号处理

目录

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

<strong># 信号处理函数的第一个参数是信号,第二个参数是当前的桢对象</strong>
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: <em>7898</em>

打开另外一个窗口,执行:

kill -USR1 <em>7898</em>

回到第一个窗口,会看到,类似下面的信息:

thread: <_MainThread(MainThread, started 140363276531520)> received signal: 10

并且程序从循环中退出了。

使用信号时,需要注意的事项[]

  • 并非所有的信号都能被程序捕获,比如SIGKILL就不能被程序捕获,SIGINT可以被应用程序捕获
  • 不同的操作系统平台,支持的信号是不同的。比如 Linux 支持SIGUSR1,但是Windows不支持
  • 每个信号都有两种表述形式:宏名 和 数字,它们是等价的。在Linux下,可以通过 kill -l 列出所有的信号
  • 在Linux下,可以通过 kill -<宏名/数字> <pid> 命令发送信号;在Python中,可以通过 os.kill(<pid>, <sig>) 函数发送信号
  • 在Python中,只有主线程能够注册信号处理函数,也只有主线程能够处理信号!!!
  • 每个信号都有自己独特的用途,比如,按下ctrl + c,会向程序发送SIGINT信号;Linux关机的时候,会向所有的进程先发送SIGTERM信号,过一段时间,再发送SIGKILL信号,强制杀死进程;子进程退出的时候,操作系统会向其父进程发送SIGCHLD信号
  • 信号处理是异步的

1,singal.pause()[]

该函数会导致进程休眠,一直到收到一个信号。同时,相应的信号处理函数也会被执行。(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 )[]

将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 )[]

如果 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 )[]

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 
<module>
 
    time.sleep(0.2)
KeyboardInterrupt

</module>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

世界因你不同

世界因你不同

李开复、范海涛 / 中信出版社 / 2010 / 29.8

这是李开复唯一的一本自传,字里行间,是岁月流逝中沉淀下来的宝贵的人生智慧和职场经验。捣蛋的“小皇帝”,11岁的“留学生”,奥巴马的大学同学,26岁的副教授,33岁的苹果副总裁,谷歌中国的创始人,他有着太多传奇的经历,为了他,两家最大的IT公司对簿公堂。而他的每一次人生选择,都是一次成功的自我超越。   透过这本自传,李开复真诚讲述了他鲜为人知的成长史、风雨兼程的成功史和烛照人生的心灵史,也首次全面......一起来看看 《世界因你不同》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具