用Python处理Unix信号

UNIX / Linux系统提供了在每个单独进程之间进行通信的特殊机制。这些机制之一是信号,属于进程之间的不同通信方法(进程间通信,缩写为IPC)。

简而言之,信号是软件中断,它被发送到程序(或进程),将重要事件或请求通知程序,以便运行特殊的代码序列。接收到信号的程序要么停止或继续执行其指令,要么在有或没有内存转储的情况下终止,甚至干脆忽略该信号。

虽然在POSIX标准中定义了它,但是实际的情况取决于开发人员如何编写脚本和实现信号处理。

在本文中,我们将解释什么是信号,向您展示如何从命令行向另一个进程发送信号,以及如何处理接收到的信号。在其他模块中,程序代码主要基于信号模块。这个模块将操作系统的according C头文件与Python连接起来

信号简介

在基于unix的系统中,有三类信号:

  • 系统信号(硬件和系统错误):SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGKILL, SIGSEGV, SIGXCPU, SIGXFSZ, SIGIO
  • 设备信号:SIGHUP、SIGINT、SIGPIPE、SIGALRM、SIGCHLD、SIGCONT、SIGSTOP、SIGTTIN、SIGTTOU、SIGURG、SIGWINCH、SIGIO
  • 用户自定义信号:SIGQUIT、SIGABRT、SIGUSR1、SIGUSR2、SIGTERM

每个信号都用一个整数值表示,可用的信号列表相当长,而且不同UNIX/Linux发行版之间不一致。在Ubuntu18.04系统上,命令kill -l显示信号列表如下:

信号1到15基本上是标准化的,在大多数Linux系统中含义如下:

  • 1 (SIGHUP): 终止连接,或重新加载守护进程的配置
  • 2 (SIGINT): 当用户希望中断该过程时,SIGINT信号由其控制终端发送到进程。这通常通过按Ctrl+C启动,但在某些系统上,可以使用"delete" 或者 "break" 替代
  • 3 (SIGQUIT): 当用户请求进程退出并执行核心转储时,SIGQUIT信号通过其控制终端发送给进程。
  • 4 (SIGILL): 当进程试图执行非法、未知或特权指令时,SIGILL信号被发送到进程。
  • 5 (SIGTRAP): 当异常发生时,SIGTRAP信号被发送到进程:调试器请求被告知的一个条件——例如,当一个特定的函数被执行时,或者当一个特定的变量改变值时。
  • 6 (SIGABRT): 异常终止
  • 7 (SIGBUS): 系统总线错误
  • 8 (SIGFPE): 算术运算错误
  • 9 (SIGKILL): 立即终止进程
  • 10 (SIGUSR1): 用户定义的信号
  • 11 (SIGSEGV): 由于非法访问内存段而导致的分割错误,它做了一个无效的虚拟内存引用,或分割故障
  • 12 (SIGUSR2): 用户定义的信号
  • 13 (SIGPIPE): 当进程试图写入没有连接到另一端的进程的管道时,SIGPIPE信号被发送到进程
  • 14 (SIGALRM):计时器终止(alarm)
  • 15 (SIGTERM): SIGTERM信号被发送到进程请求终止。与SIGKILL信号不同,它可以被进程捕获并解释或忽略。这允许进程执行良好的终止释放资源和保存状态(如果合适的话)。SIGINT与SIGTERM几乎相同。

为了向Linux终端中的进程发送信号,可以使用上面列表中的信号号(或信号名)和进程id (pid)调用kill命令。下面的示例命令向pid为12345的进程发送信号15 (SIGTERM):

$ kill -15 12345

一个等效的方法是使用信号名而不是它的编号:

$ kill -SIGTERM 12345

使用Python信号库

自Python 1.4以来,信号库是每个Python发行版的内置库。为了使用信号库,请将信号库导入Python程序,如下

import signal  

捕获并对接收到的信号做出正确的反应是由一个回调函数完成的。 一个所谓的信号处理程序。一个相当简单的信号处理程序receiveSignal()可以编写如下:

def receiveSignal(signalNumber, frame):
    print(‘Received:‘, signalNumber)
    return

这个信号处理程序只报告接收到的信号的数量。下一步是注册信号处理程序捕获的信号。对于Python程序,所有的信号(除了9,SIGKILL)都可以在您的脚本中捕获:

if __name__ == ‘__main__‘:
    # register the signals to be caught
    signal.signal(signal.SIGHUP, receiveSignal)
    signal.signal(signal.SIGINT, receiveSignal)
    signal.signal(signal.SIGQUIT, receiveSignal)
    signal.signal(signal.SIGILL, receiveSignal)
    signal.signal(signal.SIGTRAP, receiveSignal)
    signal.signal(signal.SIGABRT, receiveSignal)
    signal.signal(signal.SIGBUS, receiveSignal)
    signal.signal(signal.SIGFPE, receiveSignal)
    #signal.signal(signal.SIGKILL, receiveSignal)
    signal.signal(signal.SIGUSR1, receiveSignal)
    signal.signal(signal.SIGSEGV, receiveSignal)
    signal.signal(signal.SIGUSR2, receiveSignal)
    signal.signal(signal.SIGPIPE, receiveSignal)
    signal.signal(signal.SIGALRM, receiveSignal)
    signal.signal(signal.SIGTERM, receiveSignal)

接下来,我们添加当前进程的进程信息,并使用os模块中的getpid()方法检测进程id。在无休止的while循环中,我们等待传入的信号。我们使用另外两个Python模块来实现它——os和time。我们在Python脚本的开头也导入了它们:

import os
import time 

在主程序的while循环中,print语句输出“Waiting…”。函数调用time.sleep()使程序等待三秒钟。

    # output current process id
    print(‘My PID is:‘, os.getpid())

    # wait in an endless loop for signals
    while True:
        print(‘Waiting...‘)
        time.sleep(3)

最后,我们必须测试脚本。将脚本保存为signal-handling.py后,我们可以在终端中调用它,如下所示:

$ python3 signal-handling.py
My PID is: 5746
Waiting...
...

在第二个终端窗口中,我们向进程发送一个信号。我们通过上面屏幕上打印的进程id来标识第一个进程——Python脚本。

$ kill -1 5746

Python程序中的信号事件处理程序接收我们发送给进程的信号。它做出相应的反应,简单地确认接收到的信号:

...
Received: 1
...

忽略Signal

信号模块定义了忽略接收信号的方法。为此,信号必须与预定义的函数signal. sig_ign连接。下面的示例演示了这一点,因此Python程序不能再被CTRL+C中断。为了停止Python脚本,示例脚本中实现了另一种方法——信号SIGUSR1终止Python脚本。此外,我们使用signal.pause()方法而不是循环。它只是等待接收到一个信号。

import signal
import os
import time

def receiveSignal(signalNumber, frame):
    print(‘Received:‘, signalNumber)
    raise SystemExit(‘Exiting‘)
    return

if __name__ == ‘__main__‘:
    # register the signal to be caught
    signal.signal(signal.SIGUSR1, receiveSignal)

    # register the signal to be ignored
    signal.signal(signal.SIGINT, signal.SIG_IGN)

    # output current process id
    print(‘My PID is:‘, os.getpid())

    signal.pause()

适当地处理信号

到目前为止,我们使用的信号处理程序相当简单,只报告接收到的信号。这向我们展示了Python脚本的接口工作得很好。让我们尝试改进它。

捕捉信号已经是一个很好的基础,但是需要一些改进才能符合POSIX标准的规则。为了获得更高的准确度,每个信号都需要适当的反应(见上面的列表)。这意味着Python脚本中的信号处理程序需要通过每个信号的特定例程进行扩展。如果我们理解了信号的作用,以及一个常见的反应是什么,这种方法就会发挥最佳效果。接收信号1、2、9或15的进程终止。在任何其他情况下,它也应该编写一个核心转储。

到目前为止,我们已经实现了一个覆盖所有信号的例子,并以相同的方式处理它们。下一步是为每个信号实现一个单独的例子。下面的示例代码演示了信号1 (SIGHUP)和信号15 (SIGTERM)。

def readConfiguration(signalNumber, frame):
    print (‘(SIGHUP) reading configuration‘)
    return

def terminateProcess(signalNumber, frame):
    print (‘(SIGTERM) terminating the process‘)
    sys.exit()

上述两个函数与信号连接如下:

 signal.signal(signal.SIGHUP, readConfiguration)
 signal.signal(signal.SIGTERM, terminateProcess)

运行Python脚本,然后UNIX命令kill -1 42096kill -15 42096发送信号1 (SIGHUP)和信号15 (SIGTERM),得到如下输出:

liuf2@liuf2-virtual-machine /u/l/g/tests> python daemon.py
(‘My PID is:‘, 42096)
Waiting...
Waiting...
Waiting...
Waiting...
(SIGHUP) reading configuration
Waiting...
Waiting...
Waiting...
Waiting...
(SIGTERM) terminating the process

 

程序接收信号,并正确地处理它们。以下为完整代码:

import signal
import os
import time
import sys

def readConfiguration(signalNumber, frame):
    print (‘(SIGHUP) reading configuration‘)
    return

def terminateProcess(signalNumber, frame):
    print (‘(SIGTERM) terminating the process‘)
    sys.exit()

def receiveSignal(signalNumber, frame):
    print(‘Received:‘, signalNumber)
    return

if __name__ == ‘__main__‘:
    # register the signals to be caught
    signal.signal(signal.SIGHUP, readConfiguration)
    signal.signal(signal.SIGINT, receiveSignal)
    signal.signal(signal.SIGQUIT, receiveSignal)
    signal.signal(signal.SIGILL, receiveSignal)
    signal.signal(signal.SIGTRAP, receiveSignal)
    signal.signal(signal.SIGABRT, receiveSignal)
    signal.signal(signal.SIGBUS, receiveSignal)
    signal.signal(signal.SIGFPE, receiveSignal)
    #signal.signal(signal.SIGKILL, receiveSignal)
    signal.signal(signal.SIGUSR1, receiveSignal)
    signal.signal(signal.SIGSEGV, receiveSignal)
    signal.signal(signal.SIGUSR2, receiveSignal)
    signal.signal(signal.SIGPIPE, receiveSignal)
    signal.signal(signal.SIGALRM, receiveSignal)
    signal.signal(signal.SIGTERM, terminateProcess)

    # output current process id
    print(‘My PID is:‘, os.getpid())

    # wait in an endless loop for signals
    while True:
        print(‘Waiting...‘)
        time.sleep(3)

原文地址:https://www.cnblogs.com/tcppdu/p/10593464.html

时间: 2024-08-01 12:38:50

用Python处理Unix信号的相关文章

Python模块之信号学习(signal)

信号概述 在学习Python前应该学习下Linux下的信号,软中断信号(signal,又简称为信号)用来通知进程发生了异步事件.进程之间可以互相通过系统调用kill发送软中断信号.内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件.注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据. 信号种类 Linux中的信号种类(从图中可以看到没有32.33这俩个信号) 普通信号:1-----31号信号 实时信号:34---64号信号 通过指令      kill  信号序号

python signal(信号)

信号的概念 信号(signal)--     进程之间通讯的方式,是一种软件中断.一个进程一旦接收到信号就会打断原来的程序执行流程来处理信号. 几个常用信号: SIGINT     终止进程  中断进程  (control+c) SIGTERM   终止进程     软件终止信号 SIGKILL   终止进程     杀死进程 SIGALRM 闹钟信号 进程结束信号 SIGTERM和SIGKILL的区别 SIGTERM比较友好,进程能捕捉这个信号,根据您的需要来关闭程序.在关闭程序之前,您可以结

初步理解Python进程的信号通讯

Reference: http://www.jb51.net/article/63787.htm 信号的概念 信号(signal)--     进程之间通讯的方式,是一种软件中断.一个进程一旦接收到信号就会打断原来的程序执行流程来处理信号. 几个常用信号: SIGINT     终止进程  中断进程  (control+c) SIGTERM   终止进程     软件终止信号 SIGKILL   终止进程     杀死进程 SIGALRM 闹钟信号进程结束信号 SIGTERM和SIGKILL的区

python datetime unix时间戳以及字符串时间戳转换

将python的datetime转换为unix时间戳 import time import datetime dtime = datetime.datetime.now() ans_time = time.mktime(dtime.timetuple()) 将unix时间戳转换为python的datetime import datetime unix_ts = 1439111214.0 time = datetime.datetime.fromtimestamp(unix_ts) #!/usr/

UNIX信号

UNIX编程第10章 信号是软件中断.很多比较重要的应用程序都需处理信号.信号提供了一种处理异步事件的方法,例如,终端用户键入中断键,会通过信号机制停止一个程序,或及早终止管道中的下一个程序. 每个信号都有一个名字,这些名字都以SIG开头,例如SIGABRT是夭折信号,当进程调用abort函数时产生这种信号.SIGALARM是闹钟信号,由alarm函数设置的定时器超时后将产生此信号.在头文件<signal.h>中,信号名都被定义为正整数常量(信号编号). 不存在编号为0的信号,空信号. 很多条

浅析 Linux/UNIX 信号机制

附件:linux man手册关于signal的介绍 信号常常被称为“软中断”,和“中断”类似,用来通知程序发生异步事件.对信号的处理一般来说有三种方式:忽略,终止进程以及使用信号处理函数.信号处理函数的方式是从一处执行流断开,转而去运行另外的一处代码(信号处理),当处理函数返回时,继续从断开的地方继续执行. 1.安装信号处理函数 在系统编程的层面上与信号的处理关系最直接相关的函数有两个,他们用来安装信号处理函数: sighandler_t signal(int signum, sighandle

python 将unix文件转成dos文件

#!/usr/bin/python#-*-encoding:UTF-8-*- import sys if len(sys.argv) < 2:print('Usage : python sys.argv[0] FileName')sys.exit(1)else:print ('\nunix2dos.py execution successfully!\n') File=sys.argv[1]fileobj = open(File,'rU')try:allfile = fileobj.read()

python 在unix下json格式显示结果

在使用命令号输出接口测试结果,发现无法按照期望的json格式进行显示.查阅资料发现python自带强大的工具. 直接上代码: import os,requests url = XXXXXX content = requests.get(url).text command = "echo '%s' | python -m json.tool" % content print os.system(command) 完美的输出json格式的结果,以后再也不用复制结果到在线json转换工具上了

unix信号一览表

信号默认操作 Term 表示进程会立即结束 Core 表示进程会立即结束并进行核心转储(栈跟踪) lgn 表示进程会忽略该信号 Stop 表示进程会暂停 Cont 表示进程会回复运行 信号 值 动作 注释 SIGHUP 1  Term  由控制终端或控制进程终止时发出 SIGINT 2  Term  来自键盘的中断信号(通常是Ctrl-C) SIGQUIT 3  Core 来自键盘的退出信号(通常是Ctrl-/) SIGILL  4  Core  非法指令 SIGABRT  6  Core  来