「信号机制二」Python信号处理—signal模块

转载请注明出处: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


猜你喜欢

转载自blog.csdn.net/jinixin/article/details/80383177
今日推荐