转载请注明出处:https://blog.csdn.net/jinixin/article/details/80383177
本文是信号机制三篇记录中的第二篇,介绍Python语言中负责信号处理的signal模块,并会给出一些小demo;第一篇简单介绍了Linux信号机制,第三篇则给出关于信号的一个应用。三篇组成一个系列,想起抛砖引玉的作用,希望对大家能有所帮助。
signal模块
该模块提供Python中信号处理的机制,下面是几个常用的方法
1. signal.signal(signalnum, handler)
注册signalnum信号量的处理函数为handler
其中signalnum为待注册的信号量,handler为该信号量的处理器,其是一个可调用对象,该对象必须接受两个参数,分别是信号量signum,当前程序运行堆栈frame,这两个参数Python解释器会自动传入,因此我们不必显示传入。关于如何打印堆栈frame的值,可以参考这篇博客。
这时你也许有疑问,上篇博客中不是说程序可以忽略信号吗?
那是不是说忽略就是直接在handler的函数体中写个pass就行呢?其实这么想没有错,因为处理完信号后,程序会回到接收到信号的地方继续运行,但这样写就不简洁了。其实handler除了是可调用对象外,还可以是以下两个常量:
1)当handler为signal.SIG_DFL时,表示接收信号后,程序按系统默认行为执行;
2)当handler为signal.SIG_IGN时,则表示接收信号后,程序忽略该信号,继续自身运行。
关于该方法有两个注意点:
1)该方法是有返回值的,其将返回之前原有的信号处理函数;
2)该方法仅能在主线程中注册信号处理器,若在子线程中注册,将引发ValueError异常。
2. signal.getsignal(signalnum)
返回目前程序注册signalnum信号量的处理函数
返回值可能是Python可调用对象,signal.SIG_DFL,signal.SIG_IGN或None。
3. signal.pause()
使程序进入睡眠,直到程序接收到某个信号量
4. signal.alarm(time)
每隔time秒发出一个ALRM信号
若注册了ALRM信号的处理函数,则相关处理器会被调用。当time为0时,取消注册ALRM信号处理函数。
如何发送信号
1)如果在命令行中,可以用kill命令向对应进程发送信号,或者使用快捷键(如「CTRL-C」,Python程序会收到SIGINT信号),具体可参考上篇博客。
2)如果在Python程序中,则可借助Python的os模块:
os.kill(pid, signal):向进程号pid对应进程发送signal信号量
而进程号的获取,则可以借助下面两个方法:
os.getpid():获取程序的进程ID
os.getppid():获取程序的父进程ID
案例
案例一:注册SIGUSR1信号处理器,并在执行过程向程序发送SIGUSR1信号,调起对应处理器
#!/usr/bin/env python3
# coding=utf-8
import os
import time
import signal
import traceback
def handle_SIGUSR1(signum, frame):
print('handle sighup!{0}{1}'.format(os.linesep, '*' * 100))
print(os.linesep.join(traceback.format_stack(frame))) # 打印详细运行信息
def main():
signal.signal(signal.SIGUSR1, handle_SIGUSR1) # 注册SIGUSR1信号的处理器为handle_SIGUSR1函数
print(signal.getsignal(signal.SIGUSR1)) # 获取SIGUSR1信号目前的处理器
time.sleep(3) # 或者使用signal.pause()
os.kill(os.getpid(), signal.SIGUSR1) # 向当前进程发送SIGUSR1信号
time.sleep(3)
print('done')
if __name__ == '__main__':
main()
运行结果:
案例二:在子线程中注册某信号量的处理器,报错
#!/usr/bin/env python3
# coding=utf-8
from threading import Thread
import signal
def child():
signal.signal(signal.SIGUSR1, signal.SIG_IGN) # 注册SIGUSR1信号的处理器为signal.SIG_IGN,即忽略
print('child thread')
def main():
thread = Thread(target=child)
thread.start() # 开启子线程
thread.join()
print('done')
if __name__ == '__main__':
main()
运行结果:
案例三:注册SIGCHLD信号处理器,子进程结束执行后触发SIGCHLD信号,父进程接收
#!/usr/bin/env python3
# coding=utf-8
import os
import signal
def handle_SIGCHLD(signum, frame):
print('handle child process')
def main():
signal.signal(signal.SIGCHLD, handle_SIGCHLD) # 注册SIGCHLD信号的处理器为handle_SIGCHLD函数
pid = os.fork()
if pid == 0:
print('child process') # 子进程结束执行后,会向父进程发送SIGCHLD信号
else:
print('main process')
os.wait()
print('done')
if __name__ == '__main__':
main()
运行结果:
案例四:注册SIGALRM信号处理器,定时发送SIGALRM信号
#!/usr/bin/env python3
# coding=utf-8
import time
import signal
def handle_SIGALRM(signum, frame):
print('alarm {0}!'.format(int(time.time())))
def main():
signal.signal(signal.SIGALRM, handle_SIGALRM) # 注册SIGALRM信号的处理器为handle_SIGALRM函数
signal.alarm(3) # 设置每3秒发送一次SIGALRM信号
time.sleep(10) # 某些耗时的操作
signal.alarm(0) # 取消定时发送SIGALRM信号
print('done')
if __name__ == '__main__':
main()
运行结果:
对于信号量的用途,大家有兴趣可以看这篇博客。
文中如有不当之处,还望大家包容和指出,感谢~
参考链接:
https://docs.python.org/3/library/signal.html