信号signal

embedded/2024/11/24 17:16:46/

信号signal

信号是一种软件中断机制,基于该机制能实现进程间通信,故信号是实现进程通信的一种方式。

  • 信号由内核线程或者用户线程发起生成,例如,SIGINT 信号通常由用户通过按下 Ctrl+C 产生。

  • 内核中会保存每个进程的信息,信号生成后会根据目标进程号找到内核中的进程信息,写入信号(此处可以设置响应策略暂时阻塞信号写入)。

  • 目标进程进行系统调用时,在完成调用退出内核态之前会检查是否有信号,有信号则会根据信号按照对应方式处理

  • 操作系统为每个信号设置好了默认操作,进程可以通过自定义设置覆盖操作系统默认处理方式,或者直接忽略该信号。因此程序在接收到信号后,可能会按系统既定的默认操作执行,也可能会忽略该信号,或者使用我们编写的替代方案。但有些信号是不可被替换的

  • 不同操作系统信号信号的编号有所不同,使用信号时应当注意不同操作系统的差异

接下来将在Ubuntu操作系统上基于Python的signal模块演示信号的用处。

信号种类

信号

信号名称信号数字信号描述
SIGHUP1连接挂断
SIGINT2interrupt,中断进程执行,ctrl+c
SIGQUIT3退出进程,core dumped,ctrl+\
SIGILL4非法指令
SIGTRAP5
SIGABRT6
SIGBUS7
SIGFPE8
SIGKILL9终止进程,此信号不能被捕获或者忽略
SIGUSR110
SIGSEGV11
SIGUSR212
SIGPIPE13
SIGALRM14警告,通常是程序在一定的时间之后才生成该信号
SIGTERM15
SIGSTKFLT16
SIGCHLD17
SIGCONT18用于通知暂停的进程继续
SIGSTOP19
SIGTSTP20暂时停止进程知道接收到SIGCONT,ctrl+z
SIGTTIN21
SIGTTOU22
SIGURG23
SIGXCPU24

信号处理的应用场景

  1. 定时任务:使用信号实现定时任务,例如定时发送通知、定时检查资源等。
  2. 异步事件处理:使用信号处理异步事件,例如用户输入、网络事件等。
  3. 程序控制:使用信号控制程序的行为,例如优雅地关闭程序、处理程序错误等。

API

signalsignalsignalnum_handler_58">signal.signal(signalnum, handler)

用于设置信号处理函数

  • singnalnum:信号编号,代表具体的信号
  • handler:接收到信号后,调用handler进行处理该信号,handler允许三种情况:
    1. singal.SIG_DFL:进程采用操作系统的默认操作
    2. signal.SIG_IGN:进程忽略该信号
    3. 其他函数:按照函数进行调用,系统会自动给该函数传两个参数,一个是signalnum,一个是frame

signalgetsignalsignalnum_68">signal.getsignal(signalnum)

获取当前进程对应信号的处理函数

signalpause_72">signal.pause()

使程序进入睡眠,直到程序接收到任意一个信号

signalalarmtime_76">signal.alarm(time)

每隔time秒发出一个ALRM信号,若注册了ALRM信号的处理函数,则相关处理器会被调用。当time为0时,取消注册ALRM信号处理函数。

如何向指定进程发送信号

  1. 程序在前台运行:可以使用例如ctrl+c等快捷键发送信号

  2. 程序在后台运行:使用命令工具发送信号,例如kill命令或者Python中的os.kill

    • kill命令:
      • kill命令用于向进程发送信号,杀死进程仅仅是其一个功能
      • kill -1 pid
      • kill -9 pid
    • Python os.kill
      • os.kill(pid, signal)
      • os.getpid()

signal_95">Python的signal模块注意事项

  • Python signal模块要求所有信号handler必须在进程的主线程中注册
  • 所以进程接收到信号之后,所有回调也是在主线程进行处理
  • 主线程和子线程中任意线程均可以发送信号,包括使用signal.alarm()发送信号信号会被进程接收
  • 主线程或子线程任意线程使用signal.pause(),均可以由任意信号唤醒,该信号到达进程时唤醒所有正在等待信号的线程,信号到达之后进入等待信号状态的线程需要等待下一个信号
import signal
import threading# 在一个子线程中设置信号handler
def usr1_handler_set():signal.signal(signal.SIGUSR1, lambda signal_num, frame: ...)# Python的signal模块不支持在子线程中为进程设置信号handler# ValueError: signal only works in main threadprint("sub thread do other things")set_signal_handler_in_sub_thread = threading.Thread(target=usr1_handler_set)
set_signal_handler_in_sub_thread.start()
set_signal_handler_in_sub_thread.join()  # 等待子线程执行完毕

示例

自定义信号处理

import os
import signal
import sys
import timedef handle_int(sig, frame):print("get signal: %s, I will quit" % sig)sys.exit(0)def handle_hup(sig, frame):print("get signal: %s" % sig)if __name__ == "__main__":signal.signal(1, handle_hup)signal.signal(2, handle_int)print("PID %s" % os.getpid())while True:time.sleep(3)# kill -1 pid
# kill -9 pid

alarm信号处理

import signaldef signal_alarm_handler(signum, frame):"""处理alarm信号:param signum: 信号数字:param frame: 栈帧:return: """print(signum, type(frame))print("Now, it's the time to exit")exit()signal.signal(signal.SIGALRM, signal_alarm_handler)
signal.alarm(1)
while True:print('not yet')

查看所有信号

import signalsignals_to_names = {getattr(signal, n): nfor n in dir(signal)if n.startswith('SIG') and '_' not in n
}for s, name in sorted(signals_to_names.items()):handler = signal.getsignal(s)if handler is signal.SIG_DFL:handler = 'SIG_DFL'elif handler is signal.SIG_IGN:handler = 'SIG_IGN'print('{:<10} ({:2d}):'.format(name, s), handler)# SIGHUP     ( 1): SIG_DFL
# SIGINT     ( 2): <built-in function default_int_handler>
# SIGQUIT    ( 3): SIG_DFL
# SIGILL     ( 4): SIG_DFL
# SIGTRAP    ( 5): SIG_DFL
# SIGIOT     ( 6): SIG_DFL
# SIGBUS     ( 7): SIG_DFL
# SIGFPE     ( 8): SIG_DFL
# SIGKILL    ( 9): SIG_DFL
# SIGUSR1    (10): SIG_DFL
# SIGSEGV    (11): SIG_DFL
# SIGUSR2    (12): SIG_DFL
# SIGPIPE    (13): SIG_IGN
# SIGALRM    (14): SIG_DFL
# SIGTERM    (15): SIG_DFL
# SIGCLD     (17): SIG_DFL
# SIGCONT    (18): SIG_DFL
# SIGSTOP    (19): SIG_DFL
# SIGTSTP    (20): SIG_DFL
# SIGTTIN    (21): SIG_DFL
# SIGTTOU    (22): SIG_DFL
# SIGURG     (23): SIG_DFL
# SIGXCPU    (24): SIG_DFL
# SIGXFSZ    (25): SIG_IGN
# SIGVTALRM  (26): SIG_DFL
# SIGPROF    (27): SIG_DFL
# SIGWINCH   (28): SIG_DFL
# SIGPOLL    (29): SIG_DFL
# SIGPWR     (30): SIG_DFL
# SIGSYS     (31): SIG_DFL
# SIGRTMIN   (34): SIG_DFL
# SIGRTMAX   (64): SIG_DFL

信号的软中断特性,中断sleep执行

这是一段有助于理解sleep与信号间关系的代码

import signal
import timedef receive_alarm(signum, stack):print('Alarm :', time.ctime())time.sleep(6)print('Alarm :', time.ctime())signal.signal(signal.SIGALRM, receive_alarm)
signal.alarm(2)print('Before:', time.ctime())
time.sleep(4)
print('After :', time.ctime())# Before: Sat Nov 23 10:15:41 2024
# Alarm : Sat Nov 23 10:15:43 2024
# Alarm : Sat Nov 23 10:15:49 2024
# After : Sat Nov 23 10:15:49 2024

信号阻塞

不适用信号阻塞,执行input_10_num_and_print函数时,若kill -2 pid会导致该程序直接报错KeyboardInterrupt

使用信号阻塞,对SIGINT=2进行阻塞,则即使在执行输入部分时进行kill -2 pid,也不会直接报错KeyboardInterrupt,而是等阻塞解除后再处理信号然后报错

import os
import signaldef input_10_num_and_print(block_signal=False):if block_signal:signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGINT])# start inputnums = []for _ in range(10):nums.append(int(input()))print(nums)# end input and print numsif block_signal:signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGINT])if __name__ == '__main__':print(f"PID = {os.getpid()}")input_10_num_and_print(True) # 置True表示使用信号阻塞

子线程中的signal.pause()无法取消

import os
import signal
import threading
import time# 设置一个信号handler
def usr_handler(signal_num, frame):print("received signal %s %s" % (signal_num, threading.currentThread()))
signal.signal(signal.SIGUSR2, usr_handler)def wait_a_signal():print("wait a signal ...")signal.pause()print("received signal %s" % threading.currentThread())
wait_a_signal_thread = threading.Thread(target=wait_a_signal)
wait_a_signal_thread.start()
time.sleep(3)  # 保证子线程处于一个等待信号状态def send_signal():print("sending signal", threading.currentThread())os.kill(os.getpid(), signal.SIGUSR2)
sender = threading.Thread(target=send_signal, name="sender")
sender.start()
sender.join()signal.alarm(2)  # 先前的信号并不会触发子线程中的pause,这里需要主动退出
wait_a_signal_thread.join()

ref

https://blog.csdn.net/abc123lzf/article/details/101245167

https://docs.python.org/3/library/signal.html


http://www.ppmy.cn/embedded/140164.html

相关文章

安宝特方案 | AR助力紧急救援,科技守卫生命每一刻!

在生死时速的紧急救援战场上&#xff0c;每一秒都至关重要&#xff01;随着科技的发展&#xff0c;增强现实&#xff08;AR&#xff09;技术正在逐步渗透到医疗健康领域&#xff0c;改变着传统的医疗服务模式。 安宝特AR远程协助解决方案&#xff0c;凭借其先进的技术支持和创新…

Windows上杀掉指定端口的进程(批处理脚本方式)

前言 在Windows操作系统中&#xff0c;有时我们可能需要停止占用特定端口的程序&#xff0c;例如当您想要启动一个服务却发现所需端口已被其他程序占用时。手动查找并终止这些进程可能既耗时又容易出错。为了简化这一过程&#xff0c;我们可以编写一个批处理脚本来自动完成这项…

性能监控利器:Ubuntu 22.04 上的 Zabbix 安装与配置指南

简介 今天我们来聊聊如何在 Ubuntu 22.04 上安装和配置 Zabbix。我们会用到 PostgreSQL 作为数据库后端&#xff0c;Nginx 作为 Web 服务器&#xff0c;并用 Let’s Encrypt SSL 证书来保驾护航。 什么是 Zabbix&#xff1f; Zabbix 是一个开源的网络监控和管理解决方案&…

MacOS通过VMware Fusion安装windows 11问题汇总

环境 虚拟机&#xff0c;VMware Fusion 13.6.1本地机器&#xff0c;ARM芯片的Mac&#xff0c;系统版本14.5Windows系统镜像&#xff0c;Window11 ARM 64 bit 安装卡在WiFi连接界面 适合我本地环境的解决步骤为&#xff1a; 1、系统设置网络共享 我开启的是en5&#xff0c;这…

ArcGIS 10.2软件安装包下载及安装教程!

今日资源&#xff1a;ArcGIS 适用系统&#xff1a;WINDOWS 软件介绍&#xff1a;ArcGIS是一款专业的电子地图信息编辑和开发软件&#xff0c;提供一种快速并且使用简单的方式浏览地理信息&#xff0c;无论是2D还是3D的信息。软件内置多种编辑工具&#xff0c;可以轻松的完成地…

如何理解 TypeScript 中命名空间与模块?两者都有那些区别?如何更好的应用?

在 TypeScript 中&#xff0c;命名空间&#xff08;Namespace&#xff09;和模块&#xff08;Module&#xff09;是两种不同的代码组织方式&#xff0c;用于组织和管理代码结构&#xff0c;避免命名冲突和提高可维护性。虽然它们都可以将代码划分为不同的逻辑单元&#xff0c;但…

【layui】table的switch、edit修改

<title>简单表格数据</title><div class"layui-card layadmin-header"><div class"layui-breadcrumb" lay-filter"breadcrumb"><a>系统设置</a><a>简单表格数据</a></div> </div>&…

03系统设计之商业业务平台(D2_【秒杀抽奖】秒杀系统设计)

目录 学习前言 一、基本介绍 二、解决方案 1. 秒杀中的削峰 方案一&#xff1a;消息队列 答题器 数据过滤 2. 秒杀中的服务性能优化 三、知识小结 四、参考文献 学习前言 提示&#xff1a;这个章节会重新梳理。 一、基本介绍 秒杀请求在高度集中在某一个时间点。这…