Linux 信号signal处理机制
Signal机制在Linux中是一个非常常用的进程间通信机制,很多人在使用的时候不会考虑该机制是具体如何实现的。signal机制可以被理解成进程的软中断,因此,在实时性方面还是相对比较高的。Linux中signal机制的模型可以采用下图进行描述。
每个进程都会采用一个进程控制块对其进行描述,进程控制块中设计了一个signal的位图信息,其中的每位与具体的signal相对应,这与中断机制是保持一致的。当系统中一个进程A通过signal系统调用向进程B发送signal时,设置进程B的对应signal位图,类似于触发了signal对应中断。发送signal只是“中断”触发的一个过程,具体执行会在两个阶段发生:
1、 system call返回。进程B由于调用了system call后,从内核返回用户态时需要检查他拥有的signal位图信息表,此时是一个执行点。
2、 中断返回。进程被系统中断打断之后,系统将CPU交给进程时,需要检查即将执行进程所拥有的signal位图信息表,此时也是一个执行点。
综上所述,signal的执行点可以理解成从内核态返回用户态时,在返回时,如果发现待执行进程存在被触发的signal,那么在离开内核态之后(也就是将CPU切换到用户模式),执行用户进程为该signal绑定的signal处理函数,从这一点上看,signal处理函数是在用户进程上下文中执行的。当执行完signal处理函数之后,再返回到用户进程被中断或者system call(软中断或者指令陷阱)打断的地方。
Signal机制实现的比较灵活,用户进程由于中断或者system call陷入内核之后,将断点信息都保存到了堆栈中,在内核返回用户态时,如果存在被触发的signal,那么直接将待执行的signal处理函数push到堆栈中,在CPU切换到用户模式之后,直接pop堆栈就可以执行signal处理函数并且返回到用户进程了。Signal处理函数应用了进程上下文,并且应用实际的中断模拟了进程的软中断过程。
最近写程序,各种bug各种错,有一回程序莫名退出,没报错,也没产生日志和core文件,貌似正常退出一样。
但又不是在程序全部走完后退出,中途莫名退出,这就叫我想到了signal,应该是某些函数错误后发送kill信号给主进程,然后退出。
现在总结下signal各种类型:
Signal | Description |
SIGABRT | 由调用abort函数产生,进程非正常退出 |
SIGALRM | 用alarm函数设置的timer超时或setitimer函数设置的interval timer超时 |
SIGBUS | 某种特定的硬件异常,通常由内存访问引起 |
SIGCANCEL | 由Solaris Thread Library内部使用,通常不会使用 |
SIGCHLD | 进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略 |
SIGCONT | 当被stop的进程恢复运行的时候,自动发送 |
SIGEMT | 和实现相关的硬件异常 |
SIGFPE | 数学相关的异常,如被0除,浮点溢出,等等 |
SIGFREEZE | Solaris专用,Hiberate或者Suspended时候发送 |
SIGHUP | 发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送 |
SIGILL | 非法指令异常 |
SIGINFO | BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程 |
SIGINT | 由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程 |
SIGIO | 异步IO事件 |
SIGIOT | 实现相关的硬件异常,一般对应SIGABRT |
SIGKILL | 无法处理和忽略。中止某个进程 |
SIGLWP | 由Solaris Thread Libray内部使用 |
SIGPIPE | 在reader中止之后写Pipe的时候发送 |
SIGPOLL | 当某个事件发送给Pollable Device的时候发送 |
SIGPROF | Setitimer指定的Profiling Interval Timer所产生 |
SIGPWR | 和系统相关。和UPS相关。 |
SIGQUIT | 输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程 |
SIGSEGV | 非法内存访问 |
SIGSTKFLT | Linux专用,数学协处理器的栈异常 |
SIGSTOP | 中止进程。无法处理和忽略。 |
SIGSYS | 非法系统调用 |
SIGTERM | 请求中止进程,kill命令缺省发送 |
SIGTHAW | Solaris专用,从Suspend恢复时候发送 |
SIGTRAP | 实现相关的硬件异常。一般是调试异常 |
SIGTSTP | Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程 |
SIGTTIN | 当Background Group的进程尝试读取Terminal的时候发送 |
SIGTTOU | 当Background Group的进程尝试写Terminal的时候发送 |
SIGURG | 当out-of-band data接收的时候可能发送 |
SIGUSR1 | 用户自定义signal 1 |
SIGUSR2 | 用户自定义signal 2 |
SIGVTALRM | setitimer函数设置的Virtual Interval Timer超时的时候 |
SIGWAITING | Solaris Thread Library内部实现专用 |
SIGWINCH | 当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程 |
SIGXCPU | 当CPU时间限制超时的时候 |
SIGXFSZ | 进程超过文件大小限制 |
SIGXRES | Solaris专用,进程超过资源限制的时候发送 |
signal对应的值:
POSIX.1中列出的信号:
SIGHUP 1 A 终端挂起或者控制进程终止
SIGINT 2 A 键盘中断(如break键被按下)
SIGQUIT 3 C 键盘的退出键被按下
SIGILL 4 C 非法指令
SIGABRT 6 C 由abort(3)发出的退出指令
SIGFPE 8 C 浮点异常
SIGKILL 9 AEF Kill信号
SIGSEGV 11 C 无效的内存引用
SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道
SIGALRM 14 A 由alarm(2)发出的信号
SIGTERM 15 A 终止信号
SIGUSR1 30,10,16 A 用户自定义信号1
SIGUSR2 31,12,17 A 用户自定义信号2
SIGCHLD 20,17,18 B 子进程结束信号
SIGCONT 19,18,25 进程继续(曾被停止的进程)
SIGSTOP 17,19,23 DEF 终止进程
SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键
SIGTTIN 21,21,26 D 后台进程企图从控制终端读
SIGTTOU 22,22,27 D 后台进程企图从控制终端写
没在POSIX.1中列出,而在SUSv2列出
SIGBUS 10,7,10 C 总线错误(错误的内存访问)
SIGPOLL A Sys V定义的Pollable事件,与SIGIO同义
SIGPROF 27,27,29 A Profiling定时器到
SIGSYS 12,-,12 C 无效的系统调用 (SVID)
SIGTRAP 5 C 跟踪/断点捕获
SIGURG 16,23,21 B Socket出现紧急条件(4.2 BSD)
SIGVTALRM 26,26,28 A 实际时间报警时钟信号(4.2 BSD)
SIGXCPU 24,24,30 C 超出设定的CPU时间限制(4.2 BSD)
SIGXFSZ 25,25,31 C 超出设定的文件大小限制(4.2 BSD)
(对于SIGSYS,SIGXCPU,SIGXFSZ,以及某些机器体系结构下的SIGBUS,Linux缺省的动作是A (terminate),SUSv2 是C (terminate and dump core))。
下面是其它的一些信号
信号 值 处理动作 发出信号的原因
----------------------------------------------------------------------
SIGIOT 6 C IO捕获指令,与SIGABRT同义
SIGEMT 7,-,7
SIGSTKFLT -,16,- A 协处理器堆栈错误
SIGIO 23,29,22 A 某I/O操作现在可以进行了(4.2 BSD)
SIGCLD -,-,18 A 与SIGCHLD同义
SIGPWR 29,30,19 A 电源故障(System V)
SIGINFO 29,-,- A 与SIGPWR同义
SIGLOST -,-,- A 文件锁丢失
SIGWINCH 28,28,20 B 窗口大小改变(4.3 BSD, Sun)
SIGUNUSED -,31,- A 未使用的信号(will be SIGSYS)
(在这里,- 表示信号没有实现;有三个值给出的含义为,第一个值通常在Alpha和Sparc上有效,中间的值对应i386和ppc以及sh,最后一个值对应mips。信号29在Alpha上为SIGINFO / SIGPWR ,在Sparc上为SIGLOST。)
处理动作一项中的字母含义如下
A 缺省的动作是终止进程
B 缺省的动作是忽略此信号
C 缺省的动作是终止进程并进行内核映像转储(dump core)
D 缺省的动作是停止进程
E 信号不能被捕获
F 信号不能被忽略
代码示例:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
void when_alarm();
void when_sigint();
void when_sigchld(int);
void when_sigusr1();
void when_sigio();
int main()
{ int childpid;//子程序进程ID号 printf("程序已经开始运行,5秒钟后将接收到时钟信号。/n"); if ((childpid=fork())>0)//父进程 { signal(SIGALRM,when_alarm); //当接收到SIGALRM信号时,调用when_alarm函数 signal(SIGINT,when_sigint); //当接收到SIGINT信号时,调用when_sigint函数 signal(SIGCHLD,when_sigchld);//当接收到SIGCHLD信号时,调用when_sigchld函数 signal(SIGUSR1,when_sigusr1);//当接收到SIGUSR1信号时,调用when_sigusr1函数 signal(SIGIO,when_sigio);//当接收到SIGIO信号时,调用when_sigio函数 alarm(5); //5秒钟之后产生SIGALRM信号 raise(SIGIO); //向自己发送一个SIGIO信号 pause(); //将父进程暂停下来,等待SIGALRM信号到来 pause(); //将父进程暂停下来,等待SIGUSR1信号到来 pause(); //将父进程暂停下来,等待SIGCHLD信号到来 printf("------此时程序会停下来等待,请按下ctrl+c送出SIGINT信号-------/n"); pause(); //将父进程暂停下来,等待SIGINT信号到来 } else if(childpid==0) //子进程 { int timer; for(timer=7;timer>=0;timer--) //时钟计时5秒产生SIGALRM信号,再过2秒子进程退出,产生SIGCHLD信号 { if(timer>2) printf("距离SIGALRM信号到来还有%d秒。/n",timer-2); if(timer==4) kill(getppid(),SIGUSR1); //向父进程发送一个SIGUSR1信号 if((timer<=2)&&(timer>0)) printf("子进程还剩%d秒退出,届时会产生SIGCHLD信号。/n",timer); if(timer==0) //子进程退出,产生SIGCHLD信号 raise(SIGKILL); //子进程给自己发一个结束信号 sleep(1); //每个循环延时1秒钟 } } else printf("fork()函数调用出现错误!/n"); return 0;
}
void when_alarm()
{ printf("5秒钟时间已到,系统接收到了SIGALRM信号!/n");
}
void when_sigint()
{ printf("已经接收到了SIGINT信号,程序将退出!/n"); exit(0);
}
void when_sigchld(int SIGCHLD_num)
{ printf("收到SIGCHLD信号,表明我的子进程已经中止,SIGCHLD信号的数值是:%d。/n",SIGCHLD_num);
}
void when_sigusr1()
{ printf("系统接收到了用户自定义信号SIGUSR1。/n");
}
void when_sigio()
{ printf("系统接收到了SIGIO信号。/n");
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h> void handle_alarm(int i)
{ printf("SIGALRM num is %d\n",i);printf("handle_alarm is excuted\n");exit(0);
} void ctl_c(int j){printf("control+c is excuted\n");printf("SIGINT signal num is %d \n",j);}
int main(int argc, char *argv[])
{ signal(SIGALRM, &handle_alarm); signal(SIGINT,ctl_c);alarm(3); while(1) {}
}
运行结果:
void handle_alarm(int i) ,得到的 i 表示SIGALRM中断号14;
void ctl_c(int j) ,得到的j表示的是SIGINT中断号2
用户自定义的信号处理函数,只能有一个或者无参数,不然会报错,例如下例所示:handle_alarm函数定义两个形参,编译时报错。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h> void handle_alarm(int i,int p)
{ printf("SIGALRM num is %d\n",i);printf("p is %d \n",p);printf("handle_alarm is excuted\n");exit(0);
} void ctl_c(int j){printf("control+c is excuted\n");printf("SIGINT signal num is %d \n",j);}
int main(int argc, char *argv[])
{ signal(SIGALRM, &handle_alarm); signal(SIGINT,ctl_c);alarm(3); while(1) {}
}
相关文章:

Linux 信号signal处理机制
Signal机制在Linux中是一个非常常用的进程间通信机制,很多人在使用的时候不会考虑该机制是具体如何实现的。signal机制可以被理解成进程的软中断,因此,在实时性方面还是相对比较高的。Linux中signal机制的模型可以采用下图进行描述。 每个进程…...

SpringBoot3之Web编程
标签:Rest.拦截器.swagger.测试; 一、简介 基于web包的依赖,SpringBoot可以快速启动一个web容器,简化项目的开发; 在web开发中又涉及如下几个功能点: 拦截器:可以让接口被访问之前,将请求拦截…...

策略模式(C++)
定义 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换((变化)。该模式使得算法可独立手使用它的客户程序稳定)而变化(扩展,子类化)。 ——《设计模式》GoF 使用场景 在软件构建过程中,某些对象使用的算法可能多种多…...
【每日一题Day290】LC1281整数的各位积和之差 | 模拟
整数的各位积和之差【LC1281】 给你一个整数 n,请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。 思路:简单模拟 循环取余,计算「各位数字之积」与「各位数字之和」,最后求差返回 实现 class Solution {public…...

揭示CTGAN的潜力:利用生成AI进行合成数据
推荐:使用 NSDT场景编辑器 助你快速搭建可编辑的3D应用场景 我们都知道,GAN在生成非结构化合成数据(如图像和文本)方面越来越受欢迎。然而,在使用GAN生成合成表格数据方面所做的工作很少。合成数据具有许多好处&#x…...
GitHub中readme.md文件的编辑和使用
GitHub中readme.md文件的编辑和使用 | YuuiChungs BlogGitHub - guodongxiaren/README: README文件语法解读,即Github Flavored Markdown语法介绍...
Python 四舍五入到最接近的十位
本篇文章将讨论使用 Python 的 ceil() 函数将数字四舍五入到最接近的十。 Python 整数到最接近的十 Python 具有三个内置函数 round()、floor() 和 ceil(),可用于对数字进行舍入。 ceil() 函数属于数学模块,用于将浮点数舍入为大于或等于给定数字的最接…...

Unity限制在一个范围内移动
Unity限制在一个范围内移动 这个例子中,我们学习Vector3.ClampMagnitude的用法,限制小球在范围内移动。 在地图上放了一个小球,让他移动,但是不想让他掉下去,限制在一个球星范围内,就好像绳子拴住了一样&…...
dji uav建图导航系列(一)建图
文章目录 1、uav + rplidir雷达1.2、思岚激光雷达1.3、dji uav的launch文件2、cartographer激光建图2.1、启动文件2.2、config修改2.3、建图过程3、融合odom+laser建图1、uav + rplidir雷达 思岚激光雷达frame为base_laser_link, 无人机frame为base_footprint。 文件uav_lid…...

AAAI论文阅读
文章目录 Open-Vocabulary Multi-Label Classifcation via Multi-Modal Knowledge Transfer——知识蒸馏的范畴Med-EASi: Finely Annotated Dataset and Models for Controllable Simplifcation of Medical Texts——医学领域数据集构建“Nothing Abnormal”: Disambiguating M…...

填补5G物联一张网,美格智能快速推进RedCap商用落地
自5G R17版本标准冻结以来,RedCap一直引人注目。2023年更是5G RedCap突破性发展的一年,从首款5G RedCap调制解调器及射频系统——骁龙X35发布,到国内四大运营商发布RedCap技术白皮书,芯片厂商、模组厂商、运营商及终端企业都在积极…...
服务器杂七杂八的知识/常识归纳(不断更新)
一.pID与端口号不一样吗? pID(Process ID,进程标识符)和端口号是不同的概念。 pID是操作系统中用来唯一标识一个正在运行的进程的数字。每个正在运行的进程都会被分配一个唯一的pID,它可以用来追踪和管理进程。 而端口号是在网…...
掌握Java排序算法:实现主流排序方法与性能对比
一,C语言,主流的排序方法介绍 当谈论主流的排序方法时,通常指的是在实际应用中表现优秀且被广泛采用的排序算法。以下是常见的主流排序方法及其介绍、时间复杂度、空间复杂度和简单的C语言代码实现: 冒泡排序(Bubble S…...
jdk17 SpringBoot JPA集成多数据库
switchRegion(切换地区)功能, 客户端可手动切换地区 , 查询不同的数据库, 后台根据地区切换数据库, 请求头添加region的key 配置类 import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; im…...

vue 新学习 06 js的prototype ,export暴露,vue组件,一个重要的内置关系
01 在js中: 原型链 注意:构造函数.prototype实例化对象.__proto__,都是指向函数的原型。 export: -export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口 -import用于在一个模块中加载另一个…...

冠达管理:“高温超导”不是“室温超导”,5天4板百利电气再次澄清
短短半个月,“室温超导”在惊喜、质疑间回转,但资本市场对“超导概念股”的炒作还在进行,8月7日室温超导概念持续疯涨。同花顺显现,到8月7日收盘,18只超导概念股中,有16只股票飘红。 广东研山私募证券投资&…...

CS 144 Lab Four 收尾 -- 网络交互全流程解析
CS 144 Lab Four 收尾 -- 网络交互全流程解析 引言Tun/Tap简介tcp_ipv4.cc文件配置信息初始化cs144实现的fd家族体系基于自定义fd体系进行数据读写的adapter适配器体系自定义socket体系自定义事件循环EventLoop模板类TCPSpongeSocket详解listen_and_accept方法_tcp_main方法_in…...
Linux面试专题
Linux面试专题 1 Linux中主要有哪几种内核锁?2 Linux 中的用户模式和内核模式是什么含意?3 怎样申请大块内核内存?4用户进程间通信主要哪几种方式?5通过伙伴系统申请内核内存的函数有哪些?6) Linux 虚拟文件系统的关键数据结构有哪些?(至少写出四个)7) 对文件或设备的操作…...
MySQL错误日志(Error Log)详解
错误日志(Error Log)是 MySQL 中最常用的一种日志,主要记录 MySQL 服务器启动和停止过程中的信息、服务器在运行过程中发生的故障和异常情况等。 作为初学者,要学会利用错误日志来定位问题。下面介绍如何操作查看错误日志。 启动…...

Qt应用开发(基础篇)——LCD数值类 QLCDNumber
一、前言 QLCDNumber类继承于QFrame,QFrame继承于QWidget,是Qt的一个基础小部件。 QLCDNumber用来显示一个带有类似lcd数字的数字,适用于信号灯、跑步机、体温计、时钟、电表、水表、血压计等仪器类产品的数值显示。 QLCDNumber可以显示十进制…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...