Python Signal

信号Signal

信号Signal的全称为软中断信号,是用来通知进程发生了异步事件,是在软件层次上对中断机制的一种模拟。原理上一个进程收到一个信号与CPU收到一个中断请求可以说是类似的。

信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达。事实上进程也不知道信号到底什么时候到达,进程之间可以相互通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

信号是Linux系统编程中非常重要的概念,信号机制是进程间传递消息的一种机制,是异步进程中通信的一种方式。比如终端用户输出Ctrl+c来终端程序时会通过信号机制停止程序的执行。

信号的作用是通知进程发生了异步事件,进程之间可以调用系统传递信号,内核本身也可以发送信号给进程,告知该进程发生了某个事件。在应用层可以将消息传递给内核监控,当消息处理完毕后,内核将消息反馈给应用层,这样操作不会出现阻塞等待,保持信号处理的持续性。

相对于共享内存,信号更加偏向于系统层面,Linux系统也是通过信号管理进程的,而且系统规定了某些进程接收到某些信号后的行为 。

一个进程一旦接收到信号就会打断原来的程序执行流程来处理信号。不过需要注意要的是,信号只是用来通知某进程发生了什么事件,并不会给该进程传递任何数据。

信号生命周期

对于一个完整的信号生命周期,从信号发送到相应的处理函数执行完毕来说,,可以以分为三个阶段:信号诞生、信号在进程中注册、信号的执行和注销。

  • 信号诞生

信号事件的发生有两个来源分别是硬件来源和软件来源,最常用发送信号的系统函数是killraisealarmsettimersigqueue,软件来源还包含一些非法运算等操作。

  • 信号在目标进程中注册

在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号。内核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域中设置对应于该信号的位。如果信号发送给一个正在休眠的进程,此时如果进程睡眠在可被中断的优先级上则会唤醒进程,否则仅设置进程表中信号域相应的位而不唤醒进程。如果发送给一个处于正在运行状态的进程则只置相应的域即可。

  • 信号的执行和注销

内核处理一个进程收到的软中断信号是在该进程的上下问,因此进程必须处于运行状态。当其被信号唤醒或正常调度重新获得CPU时,再从内核空间返回到用过户空间时会检测是否有信号等待处理。如果存在未决信号等待处理且该信号没有被进程阻塞,则在运行相应的信号处理函数前,进程会将信号在未决信号链中占有的结构卸掉。

信号处理

接收信号的进程对不同的信号有三种处理方式:指定处理函数、忽略、根据系统默认值处理(大部分信号的默认处理时终止进程)。

  • 忽略信号

大多数信号可以使用忽略信号这种方式来处理,但有两种信号不能被忽略,分别是SIGKILLSIGSTOP。因为它们向内核和超级 与用户提供了进程终止和停止的可靠方法。如果忽略了,那么进程就 会变的没人能够管理,显然这是内核设计者不希望看到的场景。

  • 捕捉(指定处理函数)

需要告知内核用户希望如何处理某一种信号,简单来说就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调度用户自定义的函数,以此实现某种信号的处理。

  • 系统默认动作

对于每个信号来说,系统都对应有默认的处理动作,当发生了该信号系统会自动执行。不过对系统来说,大部分的处理方式都比较粗暴,也就是直接杀死该进程。

信号表示

每个信号都有一个名字和编号,这些名字都是以SIG开头,信号定义在signal.h头文件中,其中信号名都定义为正整数,具体的信号名称可以通过kill -l来查看。信号是从1开始编号,不存在0号信号,因为kill对信号0有特殊的应用。

对于常用的kill命令其实是一个发送信号的工具,比如使用kill -9 PID来杀死指定PID的进程,其中-9表示信号列表中9号即SIGKILL信号,表示杀死该进程的信号。

$ kill -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX    

信号分类

  • POSIX标准规则信号regular signal 1-31
  • 实时信号real-time signal 32-63

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

信号通信

  • 被动式
    内核检测到一个系统事件,比如子进程退出时会向父进程发送SIGCHLD信号,当键盘按下Ctrl+C时会发送SIGINT信号等。
  • 主动式
    比如通过系统调用kill向指定进程发送信号

常见信号

  • SIGINT 表示键盘按下Ctrl+c键时会发送给前台的每一个进程。
  • SIGQUIT 表示键盘按下Ctrl+\
  • SIGSTP 表示键盘按下Ctrl+z
  • SIGKILL 表示结束某个进程,不能被忽略处理。
  • SIGALRM 表示时钟信号,常用作定时器。
  • SIGSTOP表示暂停某个进程,且不能被忽略处理。
  • SIGCHLD表示子进程发送给父进程信号

可以在中断输入kill -l命令查看系统支持的所有信号列表

kill -l [信号声明]
kill [-s 信号声明 | -n 信号编号 | -信号声明] 进程号 | 任务声明...
  • 在信号列表中,34号之后的信号尚未定义。
  • 进程结束信号可使用SIGKILLSIGTERM
    • 对于SIGKILL结束信号时,进程是不能忽略的,该信号意味着不管进程正在做什么都必须立即停止。
    • 对于SIGTERM结束信号是比较友好的,进程能捕捉到这个信号,会根据用户的需要来关闭程序。在关闭程序之前,可以结束打开的记录文件和完成正在做的任务。在某些情况下,假如进程正在进行作业而且不能中断,那么进程可以忽略这个SIGTERM信号。

Python信号模块signal

尽管signal是Python中的模块,但主要针对UNIX平台,而Windows内核中由于对信号机制支持不充分,所以在Windows上的Python不能发会信号系统的功能。

Python的signal模块负责程序内部的信号处理,典型的操作包括信号处理函数、暂停并等待信号,定时发出SIGALRM等。

加载模块

import signal

信号名称

# 连接挂断
signal.SIGUP
# 非法指令
signal.SIGILL
# 终止进程
signal.SIGINT

SIGINT信号编号为2,当按下键盘CTRL+c组合键时进程会收到此信号,用于终止进程。

# 暂停进程CTRL+z
signal.SIGSTP
# 杀死进程,此信号不能被捕获或忽略。
signal.SIGKILL

SIGKILL信号用于强制杀死进程,此信号进程无法忽视,直接在系统层面将进程杀死,所以在Python中它是不能监听的。

# 终端退出
signal.SIGQUIT
# 终止信号,软件终止信号。
signal.SIGTERM

当终端用户输入kill sigerm pid时对应PID的进程会接收到此信号,此信号进程是可以捕获并指定函数处理。比如做一下程序清理等工作,当然也是可以忽视此信号的。

# 闹钟信号,由signal.alarm()发起。
signal.SIGALRM
# 继续执行暂停进程
signal.SIGCONT

信号处理函数

设置发送SIGALRM信号的定时器

signal.alarm(time)
  • 功能:在time秒后向进程自身发送SIGALRM信号
  • 参数:time为时间参数,单位为秒。

例如:设置时钟

$ vim sigalrm.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import signal, time

# 3秒后终止程序
signal.alarm(3)

while True:
    time.sleep(1)
    print("working")
$ python sigalrm.py
working
working
闹钟

一个进程中只能设置一个时钟,如果设置第二个则会覆盖第一个的时间,并返回第一个的剩余时间,同时第一个闹钟返回为0。

例如:当在一个程序中出现两个signal.alarm()函数时

$ vim sigalrm.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import signal, time

# 3秒后终止程序
print(signal.alarm(3)) # output:0
time.sleep(1)
print(signal.alarm(3)) # output:2

while True:
    time.sleep(1)
    print("working")
$ python sigalrm.py
0
2
working
working
闹钟

使用signal.pause阻塞函数,让进程暂停以等待信号,也就时阻塞进程执行,简单来说当接收到信号后使进程停止。

例如:

$ vim sigalrm.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import signal, time

# 3秒后终止程序
print(signal.alarm(3)) # output:0
time.sleep(1)
print(signal.alarm(3)) # output:2

# 阻塞等待信号的发生,无论什么信号都可以。
signal.pause()

while True:
    time.sleep(1)
    print("working")
$ python sigalrm.py
0
2
闹钟

使程序进入休眠

signal.pause()
  • 作用:使程序进入休眠直到程序接收到某个信号量

获取当前程序注册signalnum信号量的处理函数

signal.getsignal(signalnum)
  • 作用:获取当前程序注册signalnum信号量的处理函数
  • 返回值:可能使Python可调用对象如signal.SIG_DFLsignal.SIG_IGNNone

设置信号处理函数

signal.signal(sig, handler)
  • 功能:按照handler处理器制定的信号处理方案处理函数
  • 参数:
    • sig拟需处理的信号 ,处理信号只针对这一种信号其作用。
    • handler信号处理方案,进程可以无视信号采取默认操作也可自定义操作。

handler为下列函数时将有如下操作

  • SIG_IGN信号被无视ignore或忽略
  • SIG_DFL进程采用默认default行为处理
  • function处理器handler作为函数名称时,进程采用自定义函数处理。
  • SIGSTOP SIGKILL不能处理只能采用

例如:

$ vim signal.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import signal, time

# 3秒后终止程序
signal.alarm(3)
# 当遇到SIGINT即CTRL+C时忽略SIG_IGN
signal.signal(signal.SIGINT, signal.SIG_IGN)
# 阻塞等待信号的发生,无论什么信号都可以。
signal.pause()
$ python signal.py
闹钟

信号拦截

为什么需要设置信号拦截呢?如果使用多线程或多协程,为了防止主线程结束而子线程和子协程还在运行,此时就需要使用signal模块,使用signal模块可以绑定一个处理函数,当接收步到信号的时候不会立即结束程序。

在Python中拦截信号通常有两种方式

  • 第一种是发出kill信号
# SIGTERM 表示关闭程序信号
signal.signal(signal.SIGTERM, self._term_handler)
  • 第二种是发出CTRL+C信号
# SIGINT表示CTRL+C信号
signal.signal(signal.SIGINT, self._term_handler)

在多线程多协程的程序设计时,一般多线程或的多协程程序在设计时应该设计一个程序终止标记,当收到终止信号的时候,让终止标记状态由False修改为True,此时运行的程序只有终止标记在False状态下时才能够以运行。如果由需要休眠的协程,还应给协程增加一个休眠标记,当程序休眠的时候休眠标记设置为True,当收到终止信号的时候,不仅仅要把终止标记设置为True,还要将休眠中的协程结束掉。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • 一、什么是信号 软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用 ...
    河码匠阅读 1,825评论 0 1
  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,122评论 0 23
  • 文/tangsl(简书作者) 原文链接:http://www.jianshu.com/p/2b993a4b913e...
    西葫芦炒胖子阅读 3,761评论 0 5
  • 对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件...
    故事狗阅读 84,897评论 2 62
  • 都挺好,就是,这也有点不行,那儿也不太好。 今天还行嘛? 挺好。 哦哦。那你看起来有点儿累。 是吧,哎~ 不是说挺...
    白月之地阅读 248评论 0 0