Python之signal模块详解

匿名 (未验证) 提交于 2019-12-02 22:51:30

先简单说一下Signal是啥.(如果想直接使用可以不看)

Signal翻译过来中文就是信号- -
当然, 本身他就是Linux系统编程中非常重要的概念, 信号机制是进程之间传递消息的一种机制,

其全称为软中断信号
作用是通知进程发生了异步事件。进程之间可以调用系统来传递信号, 本身内核也可以发送信号给进程, 告诉该进程发生了某个事件.

注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。

接收信号的进程对不同的信号有三种处理方法

  1. 指定处理函数
  2. 忽略
  3. 根据系统默认值处理, 大部分信号的默认处理是终止进程

然后就是一大段类型了..
Linux系统有两大类信号

  1. POSIX标准的规则信号(regular signal 1-31编号)
  2. 实时信号(real-time signal 32-63)

规则信号

信号编号名称默认动作说明
1SIGHUP终止终止控制终端或进程
2SIGINT终止由键盘引起的终端(Ctrl-c)
3SIGQUITdump控制终端发送给进程的信号, 键盘产生的退出(Ctrl-\),
4GIGILLdusmp非法指令引起
5SIGTRAPdumpdebug中断
6SIGABRT/SIGIOTdump异常中止
7SIGBUS/SIGEMTdump总线异常/EMT指令
8SIGFPEdump浮点运算溢出
9SIGKILL终止强制杀死进程(大招, 进程不可捕获)
10SIGUSR1终止用户信号, 进程可自定义用途
11SIGSEGVdump非法内存地址引起
12SIGUSR2终止用户信号, 进程可自定义用途
13SIGPIPE终止向某个没有读取的管道中写入数据
14SIGALRM终止时钟中断(闹钟)
15SIGTERM终止进程终止(进程可捕获)
16SIGSTKFLT终止协处理器栈错误
17SIGCHLD忽略子进程退出或中断
18SIGCONT继续如进程停止状态则开始运行
19SIGSTOPֹͣ停止进程运行
20SIGSTPֹͣ键盘产生的停止
21SIGTTINֹͣ后台进程请求输入
22SIGTTOUֹͣ后台进程请求输出
23SIGURG忽略socket发送紧急情况
24SIGXCPUdumpCPU时间限制被打破
25SIGXFSZdump文件大小限制被打破
26SIGVTALRM终止虚拟定时时钟
27SIGPROF终止profile timer clock
28SIGWINCH忽略窗口尺寸调整
29SIGIO/SIGPOLL终止I/O可用
30SIGPWR终止电源异常
31SIGSYS/SYSUNUSEDdump系统调用异常

注意: 由于不同系统中同一个数值对应的信号类型不一样, 所以最好使用信号名称.
信号的数值越小, 优先级越高.

OK, 现在来说说Python中的处理

先列几个常用的信号:

编号信号名称说明
2SIGINT当按下键盘(Ctrl-c)组合键时进程就会收到这个信号
15SIGTERM当用户输入 kill sigterm pid. 对应的进程就会收到这个信号. 这个信号进程是可以捕获并指定函数处理, 例如做一下程序清理等工作. 甚至忽视这个信号
9SIGKILL强制杀死进程, 这个信号进程无法忽视, 直接在系统层面把进程杀掉. 所以在Python中他的不能监听的
14SIGALRM闹钟信号
去码

先来一个例子

#!/usr/bin/env python # -*- coding: utf-8 -*-  """     监听了SIGINT信号, 当程序在运行的时候同时按下键盘 Ctrl+c 就会输出     收到信号 2 <frame object at 0x00000000021DD048>     handler方法的两个参数分别是 信号编号, 程序帧 """  import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal  receive_times = 0  def handler(signalnum, handler):     global receive_times     print u"收到信号", signalnum, frame, receive_times     receive_times += 1     if receive_times > 3:         exit(0) # 自己走  def main():     signal.signal(signal.SIGINT, handler) # Ctrl-c     # time.sleep(10) # SIGINT 信号同样能唤醒 time.sleep, 所以这里程序就会结束     while True: # 改成 while 效果会好点        pass  if __name__ == '__main__':     main() 

再看看SIGTERM的效果

#!/usr/bin/env python # -*- coding: utf-8 -*-  """     当我们运行该程序时因为 while True 所以会持续的运行.      这里监听的是 SIGTERM 信号, 所以当我们在终端输入 kill pid (linux kill     默认是发送SIGTERM)时,      程序就会输出: 收到信号 15 <frame object at 0x7ff695738050> 0     当超过3次时就强制把自己杀死.     所以 SIGTERM 很适合用来做一些清理的工作 """  import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal  receive_times = 0  def handler(signalnum, frame):     global receive_times     print u"收到信号", signalnum, frame, receive_times     receive_times += 1     if receive_times > 3:         exit(0) # 自己走  def main():     print "pid:", os.getpid()     signal.signal(signal.SIGTERM, handler)     while True:         pass  if __name__ == '__main__':     main() 

刚才我们说过SIGKILL不能被监听.

signal.signal(signal.SIGKILL, handler)  # 这里系统会直接跑错 AttributeError: 'module' object has no attribute 'SIGKILL' 

最后来一个实际运用的例子

在python2.x的版本, 线程有个bug, 在join的时候不能接收信号
详解见:https://bugs.python.org/issue1167930
所以如果我们运行以下代码

#!/usr/bin/env python # -*- coding: utf-8 -*-  """     这里虽然我们监听了 SIGINT 信号, 但是当我们按下Ctrl-c时程序并没有任何输出. 还是要等线程运行完成程序才退出. """  import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal import threading  receive_times = 0  def handler(signalnum, frame):     global receive_times     print u"收到信号", signalnum, frame, receive_times     receive_times += 1     if receive_times > 3:         # os.kill(os.getpid(), signal.SIGTERM) # 我疯起来连自己都杀         exit(0)  def run():     print "thread %s run:"%(threading.currentThread().getName())     time.sleep(10)     print "thread %s done"%(threading.currentThread().getName())  def main():     print "pid:", os.getpid()     signal.signal(signal.SIGINT, handler)      thread_list = []     for i in range(5):         thread = threading.Thread(target = run)         thread_list.append(thread)      for thread in thread_list:         thread.start()      for thread in thread_list:         thread.join()      print "all thread done"  if __name__ == '__main__':     main() 

然后我们来改一下

#!/usr/bin/env python # -*- coding: utf-8 -*-  """     在这里我们放弃了线程的join() 方法, 然后用 while True 的方式来代替, 然后在主进程判断线程的存活状态. 这样既能持续的运行线程又能根据需求来随时中断 """  import sys reload(sys) sys.setdefaultencoding("utf-8") import time import os import signal import threading  is_run_thread = True  def handler(signalnum, frame):     print u"收到信号", signalnum, frame     global is_run_thread     is_run_thread = False # 停止运行线程  def run():     print "thread %s run:"%(threading.currentThread().getName())     while is_run_thread:         # do something         pass     print "thread %s done"%(threading.currentThread().getName())  def main():     print "pid:", os.getpid()     signal.signal(signal.SIGINT, handler)      thread_list = []     for i in range(5):         thread = threading.Thread(target = run)         thread_list.append(thread)      for thread in thread_list:         thread.start()     # 注意这里     while True:         for thread in thread_list:             if thread.isAlive():                 break         else:             break      # for thread in thread_list:     #     thread.join()      print "all thread done"  if __name__ == '__main__':     main() 

注意, 在wnidows系统中只能调用 SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, or SIGTERM



作者:thisgf
链接:https://www.jianshu.com/p/c8edab99173d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!