当前位置: 首页 > news >正文

一文搞定Linux信号

Linux信号概述

信号是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。

Linux信号可由如下条件产生:

对于前台进程,用户可以通过输人特殊的终端字符来给它发送信号。比如输入Ctrl+C 通常会给进系统异常。

比如浮点异常和非法内存段访问。

系统状态变化。比如 alarm定时器到期将引起SIGALRM信号。

运行kill命令或调用kill函数。

发送信号

#include<sys/types.h>
#include<signal.h>
int kill(pid_t pid,int sig);

kill函数的pid参数以及意义

pid参数含义
pid>0信号发送给PID为pid的进程
pid=0信号发送给本进程组内的所有进程
pid=-1信号发送给楚init进程外所有进程,但发送者需要拥有对目标进程发送信号的权限
pid<-1信号发送给组ID为-pid的进程组中所有成员

Linux定义的信号值都大于0,如果sig取值为0,则kill函数不发送任何信号。但将sig设置为О可以用来检测目标进程或进程组是否存在,因为检查工作总是在信号发送之前就执行。不过这种检测方式是不可靠的。一方面由于进程PID的回绕,可能导致被检测的PID不是我们期望的进程的PID:

另一方面,这种检测方法不是原子操作。

函数成功返回0,失败则返回errno,几种可能如下表格

kill出错情况
error含义
EINVAL无效的信号
EPERM该进程没有权限发送信号给任何一个目标进程

ESRCH

目标进程或进程组不存在

信号处理方式

接受信号就要处理信号

#include<signal.h>
typedef void (*_sighandler_t)(int);

#include<bits/signum.h>
#define SIG_DFL ((_sighandler_t) 0);
#define SIG_ING ((_sighandler_t) 1);

信号处理函数只带有一个整型参数,该参数用来指示信号类型。信号处理函数应该是可重入的,否则很容易引发一些竞态条件。所以在信号处理函数中严禁调用一些不安全的函数。

SIG_IGN表示忽略目标信号,SIG_DFL表示使用信号的默认处理方式。信号的默认处理方式有如下几种:结束进程(Term)、忽略信号(Ign)、结束进程并生成核心转储文件( Core)、暂停进程( Stop),以及继续进程(Cont)。

中断系统的调用

如果程序在执行处于阻塞状态的系统调用时接收到信号,并且我们为该信号设置了信号处理函数,则默认情况下系统调用将被中断,并且errno被设置为EINTR。我们可以使用sigaction函数(见后文)为信号设置SA_RESTART标志以自动重启被该信号中断的系统调用。
对于默认行为是暂停进程的信号(比如 SIGSTOP、SIGTTIN),如果我们没有为它们设置信号处理函数,则它们也可以中断某些系统调用(比如 connect、epoll_wait)。POSIX没有规定这种行为,这是Linux独有的。

信号函数

signal的系统调用

#include<signal.h>

_sighandler_t signal(int sig,_sighandler_t  _handler);

sig 参数指出要捕获的信号类型。_handler参数是_sighandler_t类型的函数指针,用于指定信号sig 的处理函数。

signal函数成功时返回一个函数指针,该函数指针的类型也是_sighandler_t。这个返回值是前一次调用signal函数时传入的函数指针,或者是信号sig对应的默认处理函数指针SIG_DEF(如果是第一次调用signal 的话)。

sigaction

#include<siganl.h>
int sigaction(int sig,const struct sigaction* act,struct sigaction* oact);

          struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

该结构体中的sa_hander成员指定信号处理函数。sa_mask成员设置进程的信号掩码(确切地说是在进程原有信号掩码的基础上增加信号掩码),以指定哪些信号不能发送给本进程。sa_mask 是信号集sigset_t (_sigset_t的同义词)类型,该类型指定一组信号。关于信号集,我们将在后面介绍。sa_flags 成员用于设置程序收到信号时的行为,其可选值如表下图所示。

sa_flags
选项含义
SA_NOCLDSTOP设置成表示该子进程暂停时不生成SIGCHLD
SA_NOCLDWAIT子进程结束时不产生僵尸进程
SA_SIGINFO使用sa_sigaction作为信号处理函数(而不是默认sa_hanler),提供更多的信息
SA_ONSTACK调用由sigaltstack函数设置的可选信号栈上的信号函数
SA_RESTART重新调用被信号终止的系统调用
SA_NODEFER当接收到信号并进入其信号处理函数,不屏蔽信号,默认情况下,我们期望在进程处理一个信号时不在接受到同种信号
SA_RESETHAND信号处理函数执行完,恢复信号的默认处理方式
SA_INTERRUPT中断系统调用
SA_NOMSAK同SA_NODEFER
SA_ONESHOT同SA_RESETHAND
SA_STACK同SA_ONSTACK

 

sa_restorer成员已经过时啦

信号集

信号集函数

信号集原型:

 #include <signal.h>

       int sigemptyset(sigset_t *set);

       int sigfillset(sigset_t *set);

       int sigaddset(sigset_t *set, int signum);

       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);

进程信号掩码

 #include <signal.h>

  int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

_set参数指定新的信号掩码,_oset参数则输出原来的信号掩码(如果不为NULL的话)。如果_set参数不为NULL,则_how参数指定设置进程信号掩码的方式,

hou参数
_how参数含义
SIG_BLOCK新的进程信号就是当前值和_set指定信号的并集
SIG_UNBLOCK新的进程信号就是当前值和_set指定信号的交集
SIG_SETMAsk直接将进程信号掩码设置成_set

 

注意:如果_set为NULL,则进程信号掩码不变,此时我们仍然可以利用_oset参数来获得进程当前的信号集

被挂起的信号

设置进程信号掩码后,被屏蔽的信号将不能被进程接收。如果给进程发送一个被屏蔽的信号,则操作系统将该信号设置为进程的一个被挂起的信号。如果我们取消对被挂起信号的屏蔽,则它能立即被进程接收到。如下函数可以获得进程当前被挂起的信号集:

#include<signal.h>
int sigpending(sigset_t* set);

set参数用于保存被挂起的信号集。显然,进程即使多次接收到同一个被挂起的信号,sigpending 函数也只能反映一次。并且,当我们再次使用sigprocmask使能该挂起的信号时,该信号的处理函数也只被触发一次。

关于信号和信号集,Linux还提供了很多有用的API,这里就不一一介绍了。需要提醒读者的是,要始终清楚地知道进程在每个运行时刻的信号掩码,以及如何适当地处理捕获到的信号。在多进程、多线程环境中,我们要以进程、线程为单位来处理信号和信号掩码。我们不能设想新创建的进程、线程具有和父进程、主线程完全相同的信号特征。比如,fork调用产生的子进程将继承父进程的信号掩码,但具有一个空的挂起信号集

同一事件源

信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线。很显然,信号处理函数需要尽可能快地执行完毕,以确保该信号不被屏蔽(前面提到过,为了避免一些竞态条件,信号在处理期间,系统不会再次触发它〉太久。

一种典型的解决方案是;把信号的主要处理逻辑放到程序的主循环中,当信号处理函数被触发时,它只是简单地通知主循环程序接收到信号,并把信号值传递给主循环,主循环再根据接收到的信号值执行目标信号对应的逻辑代码。信号处理函数通常使用管道来将信号“传递”给主循环﹔信号处理函数往管道的写端写入信号值,主循环则从管道的读端读出该信号值。那么主循环怎么知道管道上何时有数据可读呢﹖这很简单,我们只需要使用IO复用系统调用来监听管道的读端文件描述符上的可读事件。如此一来,信号事件就能和其他I/O事件一样被处理,即统一事件源

网络编程相关的信号

SIGHUP

控制终端被挂起

当挂起进程的控制终端时,SIGHUP信号将被触发。对于没有控制终端的网络后台程序而言,它们通常利用SIGHUP信号来强制服务器重读配置文件。一个典型的例子是xinetd超级服务程序。

SIGPIPE

往读端被关闭的管道写入数据或socket连接写数据

默认情况下,往一个读端关闭的管道或socket连接中写数据将引发SIGPIPE信号。我们需要在代码中捕获并处理该信号,或者至少忽略它,因为程序接收到SIGPIPE信号的默认行为是结束进程,而我们绝对不希望因为错误的写操作而导致程序退出。引起SIGPIPE信号的写操作将设置errno为EPIPE。

这个博客,我们可以使用send函数的MSG_NOSIGNAL标志来禁止写操作触发

SIGURG

检测带外数据到达

附录:LINUX信号大全

相关文章:

  • 跟着MindSpore一起学习深度概率
  • 模型的动态LOD优化
  • 人工智能学习日记------KNN分类
  • Salesforce撤离中国后,谁来缓解在华跨国企业的焦虑?
  • 分布式系列精讲 分布式系统和单体系统之间到底有什么区别?
  • 什么是物联网数据采集网关?物联网数据采集网关的特点
  • 【vue3】05. 跟着官网学习vue3
  • 金九银十,阿里高级测开给面试者的十大建议
  • 使能OpenHarmony富设备产品化落地,润和软件HH-SCDAYU110通过兼容性测评
  • Docker 之 高级篇
  • JS | “购物车”增、删、改、查的案例
  • MySql截取字符串的几个常用函数详解
  • 多线程-线程与进程、线程的实现方式(第十八天)
  • STL:string容器详解
  • [Linux]进程间通信(system V共享内存 | system V信号量)
  • 【刷算法】求1+2+3+...+n
  • 2017届校招提前批面试回顾
  • extjs4学习之配置
  • iOS | NSProxy
  • IOS评论框不贴底(ios12新bug)
  • JavaScript中的对象个人分享
  • js如何打印object对象
  • Redis字符串类型内部编码剖析
  • SpiderData 2019年2月23日 DApp数据排行榜
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • WePY 在小程序性能调优上做出的探究
  • 代理模式
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 怎么把视频里的音乐提取出来
  • mysql面试题分组并合并列
  • 关于Android全面屏虚拟导航栏的适配总结
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • #Spring-boot高级
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (BFS)hdoj2377-Bus Pass
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • ******之网络***——物理***
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • .NET开发人员必知的八个网站
  • .NET中winform传递参数至Url并获得返回值或文件
  • .net中生成excel后调整宽度
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • @param注解什么意思_9000字,通俗易懂的讲解下Java注解
  • [ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务
  • [Android Pro] android 混淆文件project.properties和proguard-project.txt