注意:后续技术分享,第一时间更新,以及更多更及时的技术资讯和学习技术资料,将在公众号CTO Plus发布,请关注公众号:CTO Plus
Python标准库系列将分享90个日常常用的内置模块,从介绍、特性和作用、使用方法以及应用场景来介绍标准库的使用,代码示例在最新的Python3.11版本上运行,其他低版本Python可能存在差异。
标准库-内置库(Standard Library):内置库是指Python自带的标准库,包含了大量常用的模块和函数,例如`os`、`sys`、`re`、`math`、`random`等。这些模块和函数可以直接import进来使用,无需下载或安装。
第三方库(Third-party Library):第三方库是指由第三方开发者编写的Python库。这些库可以通过pip或conda等包管理工具进行下载和安装,例如`numpy`、`pandas`、`matplotlib`、`django`等。即需要pip install安装后才能使用的都是第三方库。
在前面的文章《Python基础之开发必备-标准库(内置模块)汇总详细介绍》中介绍过Python有上百个标准库(内置模块),本篇的signal即是常用的模块之一。其他更多的Python3标准库实践资料请关注公众号:CTO Plus查看。
查看标准库清单:https://mp.weixin.qq.com/s/E4HD4_aPWx0Ok3s-Uf28NQ
简介
Python3标准库中的signal模块允许Python程序获取操作系统发出的信号并作出相应的响应。 在Python程序中,信号通常用于向运行中的进程发送中断或终止指令,或者在异步程序中做出响应。 这种模块的应用是非常广泛的,因为几乎所有的操作系统都使用信号与进程进行通信。
signal包负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂停并等待信号,以及定时发出SIGALRM等。要注意,signal包主要是针对UNIX平台(比如Linux, MAC OS),而Windows内核中由于对信号机制的支持不充分,所以在Windows上的Python不能发挥信号系统的功能。
Windows上执行出错
Linux上的执行结果
在多个进程的通信机制中,只有singal是异步执行的,另外python进程间通信的机制还有pipe(管道),queue(队列),value(共享空间)等等。
Linux的信号
Linux以进程为单位来执行程序。信号是由内核(kernel)管理的。信号的产生方式多种多样,它可以是内核自身产生的,比如出现硬件错误(比如出现分母为0的除法运算,或者出现segmentation fault),内核需要通知某一进程;也可以是其它进程产生的,发送给内核,再由内核传递给目标进程。
信号是通过注册的方式“挂”在一个进程中的,并且不会阻塞该进程的运行,一个进程一旦接收到其他进程(可能是应用中的其他进程,也可能使操作系统中的进程)发送的信号就会打断原来的程序执行流程来处理这个信号。
内核中针对每一个进程都有一个表存储相关信息。当内核需要将信号传递给某个进程时,就在该进程相对应的表中的适当位置写入信号,这样,就生成(generate)了信号。当该进程执行系统调用时,在系统调用完成后退出内核时,都会顺便查看有没有发送过来的信号。如果有信号,进程会执行对应该信号的操作(signal action, 也叫做信号处理signal disposition),此时叫做执行(deliver)信号。从信号的生成到信号的传递的时间,信号处于等待(pending)状态。我们同样可以设计程序,让其生成的进程阻塞(block)某些信号,也就是让这些信号始终处于等待的状态,直到进程取消阻塞(unblock)或者无视信号。
Linux的信号是一种进程间通信的方式,它可以用于进程之间的通讯和控制。在Linux中,每个进程都有一个唯一的进程ID(PID),每个进程可以接收和发送信号。信号是Linux中的一种异步事件,它可以由内核、其他进程或用户发送给进程。
Linux中的信号有很多种,每种信号都有一个唯一的标识符(信号号),它们可以在程序运行时被捕获和处理,信号所传递的每一个整数都被赋予了特殊的意义,并有一个信号名对应该整数。下面是一些常见的Linux信号:
1. SIGINT(2):表示中断(INTERRUPT)信号,通常由用户在终端上按下Ctrl+C键从shell中发出信号发送给进程,用于终止正在运行的进程。
2. SIGTERM(15):表示终止信号,通常由系统管理员或其他进程发送给进程,用于请求进程正常终止。
3. SIGKILL(9):表示强制终止信号,它可以强制终止进程,即使进程正在执行某些关键操作。
4. SIGALRM(14):表示闹钟信号,起到定时器的作用,它用于在指定的时间间隔内发送给进程,用于定时器和超时处理。
5. SIGUSR1(10)和SIGUSR2(12):表示用户自定义信号,可以由用户在程序中定义和发送。
6. SIGQUIT(3):当键盘按下CTRL+\从shell中发出信号,信号被传递给shell中前台运行的进程,对应该信号的默认操作是退出 (QUIT) 该进程。
7. SIGCONT(18):用于通知暂停的进程继续。
8. SIGTSTP(20):当键盘按下CTRL+Z从shell中发出信号,信号被传递给shell中前台运行的进程,对应该信号的默认操作是暂停 (STOP) 该进程。
其他更多的Linux信号编号和介绍,在公众号:CTO Plus中进行查看。
Linux中的信号可以使用kill命令发送给进程,也可以使用系统调用kill()在程序中发送。进程可以使用signal()或sigaction()函数注册信号处理程序来捕获和处理信号。信号处理程序是一个函数,它在接收到信号时被调用,可以执行一些特定的操作,如打印日志、保存数据或终止进程等。
需要注意的是,Linux中的信号是异步事件,进程不能保证在接收到信号时立即响应。另外,一些信号是不可靠的,即它们可能会丢失或重复发送。因此,在处理信号时需要小心,确保程序的正确性和稳定性。
Linux上可以用过命令kill –l查看所有的信号
特点和作用
1. 可以向运行中的进程发送特定信号,如终止进程的SIGTERM信号。
2. 提供了一个处理信号的机制,并提供与信号相关的信息,例如,可以通过signal.SIGINT获取CTRL+C按下通知的信号。
3. 在异步编程中,允许程序在接收信号时采取措施,例如,如果系统中的某个进程被意外终止,则可以通过信号处理器中的代码使程序安全退出。
常用的方法和代码示例
signal包的核心是使用signal.signal()函数来预设(register)信号处理函数。
singnal.signal(signalnum, handler)
signalnum为某个信号,handler为该信号的处理函数。我们在信号基础里提到,进程可以无视信号,可以采取默认操作,还可以自定义操作。当handler为signal.SIG_IGN时,信号被无视(ignore)。当handler为singal.SIG_DFL,进程采取默认操作(default)。当handler为一个函数名时,进程采取函数中定义的操作。
1.定义信号处理器
import signal, sys
# 定义信号处理器函数,用于处理SIGINT信号
def signal_handler(signal, frame):
print('Hello SteveRocket, You pressed Ctrl+C!')
sys.exit(0)
# 设置信号处理器
signal.signal(signal.SIGINT, signal_handler)
signal.pause()
终端上运行该程序,当程序运行到signal.pause()的时候,进程暂停并等待信号。此时,通过按下CTRL+C向该进程发送SIGINT信号。我们可以看到,进程执行了signal_handler()函数, 随后返回主程序,继续执行。(当然,也可以用ps查询process ID, 再使用$kill来发出信号。),进程并不一定要使用signal.pause()暂停以等待信号,它也可以在进行工作中接受信号,比如将上面的signal.pause()改为一个需要长时间工作的循环。
# 预设信号处理函数
def signal_handler2(signal, frame):
print('I received: ', signal)
# register signal.SIGTSTP's handler
signal.signal(signal.SIGTSTP, signal_handler2)
signal.pause()
print('End of Signal Demo')
使用signal.signal()函数来预设信号处理函数,然后执行signal.pause()来阻塞进程,让该进程暂停以等待信号。当信号SIGUSR1被传递给该进程时,进程从暂停中恢复(停止阻塞),并根据预设,执行SIGTSTP的信号处理函数signal_handler2()。signal_handler2()的两个参数一个用来识别信号(signal),另一个用来获得信号发生时,进程栈的状况(stack frame),这两个参数都由signal.singnal()函数来传递。
我们可以根据自己的需要更改myHandler()中的操作,以针对不同的信号实现更多个性化的处理。
2.向进程发送信号来终止进程
import signal, os
# 向进程本身发送SIGTERM信号
os.kill(os.getpid(), signal.SIGTERM)
3.忽略信号
import signal
# 忽略SIGPIPE信号(在网络编程中,可能会收到这种信号)
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
4.在异步程序中等待信号
import signal
# 处理异步IO操作的类
class AsyncIO:
def __init__(self):
self.stopped = False
# 定义信号处理器,用于处理中断信号
def signal_handler(self, signal, frame):
self.stopped = True
def run(self):
# 注册信号处理器
signal.signal(signal.SIGINT, self.signal_handler)
# 构建异步IO事件循环
loop = asyncio.get_event_loop()
while not self.stopped:
# 等待异步IO发生事件
try:
loop.run_forever()
except KeyboardInterrupt:
self.stopped = True
loop.close()
5. 定时发出SIGALRM信号
一个有用的函数是signal.alarm(),它被用于在一定时间之后,向进程自身发送SIGALRM信号
import signal
def signal_handler(sighandler, frame):
"""
Define signal handler function
"""
print("Now it's the time")
exit()
# register signal.SIGALRM's handler
signal.signal(signal.SIGALRM, signal_handler)
signal.alarm(10)
while 1:
pass
我们这里用了一个无限循环以便让进程持续运行。在signal.alarm()执行5秒之后,进程将向自己发出SIGALRM信号,随后,信号处理函数myHandler开始执行。
6. 发送信号
signal包的核心是设置信号处理函数。除了signal.alarm()向自身发送信号之外,并没有其他发送信号的功能。但在os包中,有类似于linux的kill命令的函数,分别为
发送一个信号给某个进程
os.kill(pid, sid)
发送一个信号给某个进程组
os.killpg(pgid, sid)
参数解析:
pid:指定发送信号的进程号
sig:为信号所对应的整数,要发送的信号代号(需要通过signal模块获取) 或者singal.SIG*
实际上signal、pause、kill和alarm都是Linux应用编程中常见的C库函数,在这里,我们只不过是用Python语言来实现了一下,Python的解释器是使用C语言来编写的,所以有此相似性也并不意外。此外,在Python 3.4中,signal包被增强,信号阻塞等功能被加入到该包中。后面的文章中将详细介绍。
应用场景
1. 处理中断信号:当你按下CTRL+C停止进程时,你可以通过signal模块捕获SIGINT信号,以优雅地终止进程。
2. 优雅退出:在Python服务中,如果接收到SIGTERM信号(停止进程的信号),您可以使用signal模块中的处理器来优雅地关闭正在运行的进程。
3. 异步编程:如果使用异步编程,您可以使用signal模块来等待信号,在接收到信号时运行特定的代码。
总之,signal模块是一个非常有用的Python标准库,它允许Python程序员在操作系统层面上与进程进行通信,从而使程序更为可靠和简洁。它尤其是在处理中断信号,在优雅退出,以及在异步编程中等待信号方面,是一个非常有用的工具。
完整的Python3.11标准库代码示例,请关注公众号CTO Plus,在后台回复:py标准库
Python专栏
https://blog.youkuaiyun.com/zhouruifu2015/category_5742543
更多资料 · 微信公众号搜索【CTO Plus】关注后,获取更多,我们一起学习交流。
关于公众号的描述访问如下链接
更多精彩,关注我公号,一起学习、成长
标准库系列-推荐阅读:
推荐阅读: