Linux进程信号

本文详细介绍了Linux系统的信号机制,包括信号的概述、记录与发送、产生、默认行为、自定义处理函数、核心转储、异常信号、信号处理方式、阻塞与恢复、信号处理函数sigaction以及volatile关键字的作用。通过对信号的深入理解,有助于更好地调试和管理进程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

信号概述

像上课铃声这种信号,我们识别接收后,知道该上课了,这是我们后天学习养成的默认意识

在进程收到信号后,它是知道该怎么做的 ,程序员内置了默认的处理行为

进程的运行跟信号的产生属于异步关系:
1.进程不一定立刻去处理已经到来的信号
2.如果进程在处理优先级更高的事情,可以暂时不处理信号,等到合适的时候再处理。
3.会用某种方式记录下已经到来的但没处理的信号,以便在空闲的时候处理这些信号

异步:二者之间互不影响
同步:二者之间相互影响

处理信号的三种方式:
1.默认行为

2.提供信号处理函数,要求内核在处理该信号时切换到用户态来执行这个处理函数,称为捕捉信号

3.忽略

kill -l 查看信号
在这里插入图片描述
总共有62个信号(1-31普通信号,34-64实时信号)
在这里插入图片描述

信号的记录与发送

信号是在进程的task_struct中记录的,通过位图来记录是否产生信号

所以进程收到信号,本质是操作系统修改了进程中的信号位图(只能是OS)

操作系统是软硬件资源的管理者

信号的产生

signal函数(自定义行为)

在这里插入图片描述
示例:
ctrl c(2号信号) 在这里插入图片描述在这里插入图片描述
通过键盘输入ctrl+c(2号信号)来给进程发送信号

ctrl c(键盘产生的信号)只能发送给前台进程,一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
可以用kill发送信号给后台进程
并不是所有的信号都能捕捉,比如9号就不能(全部捕捉就意味着可能不能杀死进程)

产生信号

Core Dump

SIGINT(Term)的默认处理动作是终止进程
SIGQUIT(Core)的默认处理动作是终止进程并且Core Dump

在这里插入图片描述
用3号信号关掉进程的时候,会进行核心转储

查看系统当中的系统资源:ulimit - a
在这里插入图片描述
云服务器下的核心转储是默认关闭的(why:每次运行程序挂掉都会在磁盘产生不小的core.pid文件)

我们把它打开:
在这里插入图片描述
再次运行程序向进程发出三号信号(Ctrl+\)
在这里插入图片描述
多产生一个core文件,5865是发生核心转储的进程ID
在这里插入图片描述

核心转储:
代码运行中出错时,将进程内存中的核心数据转储到磁盘上,生成core.pid文件
目的是为了调试定位问题

程序异常产生信号

利用core文件进行事后调试除0异常
在这里插入图片描述
请添加图片描述使用命令core-file core.pid 查看错误信息在这里插入图片描述
可以看到被8号信号(SIGFPE)终止,15行报错
在这里插入图片描述
进程为什么会崩溃
程序崩溃的本质是收到了OS发送的信号

进程为什么会收到信号
当程序发生某种错误时,一定会在硬件层面上有所表现,进而被OS识别,向该进程发出信号

status(16位)

在这里插入图片描述

子进程正常退出:
在这里插入图片描述
在这里插入图片描述
子进程异常退出:
在这里插入图片描述

在这里插入图片描述
子进程收到8号信号,并且coredump为1,说明运行时程序崩溃时core dump了

ctrl+z信号

ctrl+z(20号信号)暂停进程,把进程放到后台

jobs:查看后台进程
fg 序号 将后台恢复到前台

在这里插入图片描述

系统调用产生信号

kill

在这里插入图片描述
模拟实现kill函数(利用命令行参数)

命令行参数:
在这里插入图片描述
将字符串转为int在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到通过命令行参数,在程序里面进行系统调用kill,依然产生了信号

raise

在这里插入图片描述
作用:自己给自己发信号

示例:
在这里插入图片描述
在这里插入图片描述

abort

在这里插入图片描述

abort函数使当前进程接收到信号而异常终止
特性:就像exit函数一样,abort函数总是会成功的,所以没有返回值

示例:
在这里插入图片描述

在这里插入图片描述

软件条件产生的信号

SIGPIPE是一种由软件条件产生的信号

alarm

在这里插入图片描述
在传入时间后,向进程发送14号信号
在这里插入图片描述
不像abort,捕获后并没有终止进程
在这里插入图片描述
每隔一秒打印递增的count:
每次alarm发出信号捕获后,有设置新的闹钟,就会一直打印
在这里插入图片描述
在这里插入图片描述

信号的保存

1.实际执行信号的处理动作称为信号递达
2.信号从产生到递达之间的状态,称为信号未决
3.进程可以选择阻塞某个信号
4.被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作

收到信号后:在这里插入图片描述
所以在阻塞时(1到31号信号)收到多次该信号只处理一次

修改位图

sigset_t(信号集)
作用:用于描述进程的block位图,pending位图的信号集

#include <signal.h>
int sigemptyset(sigset_t *set);//清空
int sigfillset(sigset_t *set);// 全部置1
int sigaddset (sigset_t *set, int signo);//添加信号到信号集
int sigdelset(sigset_t *set, int signo);//删除信号到信号集
int sigismember(const sigset_t *set, int signo);//判断信号是否存在

在这里插入图片描述

系统调用让设置能够修改PCB里面的内容:

①sigprocmask

sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1

how:如何修改当前信号集

SIG_BLOCK添加set中的包含的信号到信号集
SIG_UNBLOCK解除信号集中set所包含的信号
SIG_SETMASK将set拷贝给当期阻塞信号集

set:用set信号集来修改当前阻塞信号集
oset:阻塞信号集会先备份到oset里面,是输出型参数,方便恢复信号集

②sigpending

在这里插入图片描述
获取当前进程的未决信号集,通过set参数传出(输出型参数)。调用成功则返回0,出错则返回-1

示例:

在这里插入图片描述
在这里插入图片描述
可以看出:先屏蔽了2号信号,想进程发送2号信号不会被递达,pending对应位置修改为1

示例二:
在这里插入图片描述

在这里插入图片描述
先阻塞了2号信号,发送二号信号,pending修改,count==6时恢复了2号信号,2号信号递达,执行捕捉代码,最后信号执行完,对应pending修改为0

信号的处理

上面提到进程收到信号之后,不是立即处理信号,而是在合适的时候
这个合适的时候就是内核态切换回用户态的时候

内核态通常执行OS代码,权限优先级非常高
用户态执行普通用户的代码的状态,受OS的管理

在这里插入图片描述
在这里插入图片描述

自定义处理函数状态切换4次
默认,忽略只切换了两次

sigaction

int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
作用:自定义捕捉信号
类似于sigprocmask

struct sigaction:
在这里插入图片描述
sa_sigaction:处理实时信号的接口
sa_handler:捕捉执行的方法(如果设置成SIG_DFL表示执行系统默认动作)
sa_flags:通常设置为0
sa_mask:说明需要额外屏蔽的信号

当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字(自动添加到mask中),当信号处理函数返回时自动恢复原来的信号屏蔽字

示例:

在这里插入图片描述
在这里插入图片描述

volatile关键字

让以下代码在优化级别-O3 下运行
在这里插入图片描述

在这里插入图片描述
加上volatile后
在这里插入图片描述
在这里插入图片描述
就会捕获信号退出循环
在这里插入图片描述
加入volatile后,不会将flag先置入寄存器,而是在读取内存中的值到寄存器中再检测

volatile的作用:保证了内存可见性

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值