像上课铃声这种信号,我们识别接收后,知道该上课了,这是我们后天学习养成的默认意识
在进程收到信号后,它是知道该怎么做的 ,程序员内置了默认的处理行为
进程的运行跟信号的产生属于异步关系:
1.进程不一定立刻去处理已经到来的信号
2.如果进程在处理优先级更高的事情,可以暂时不处理信号,等到合适的时候再处理。
3.会用某种方式记录下已经到来的但没处理的信号,以便在空闲的时候处理这些信号
异步:二者之间互不影响
同步:二者之间相互影响
处理信号的三种方式:
1.默认行为
2.提供信号处理函数,要求内核在处理该信号时切换到用户态来执行这个处理函数,称为捕捉信号
3.忽略
kill -l 查看信号
总共有62个信号(1-31普通信号,34-64实时信号)
信号是在进程的task_struct中记录的,通过位图来记录是否产生信号
所以进程收到信号,本质是操作系统修改了进程中的信号位图(只能是OS)
操作系统是软硬件资源的管理者
示例:
ctrl c(2号信号)
通过键盘输入ctrl+c(2号信号)来给进程发送信号
ctrl c(键盘产生的信号)只能发送给前台进程,一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
可以用kill发送信号给后台进程
并不是所有的信号都能捕捉,比如9号就不能(全部捕捉就意味着可能不能杀死进程)
SIGINT(Term)的默认处理动作是终止进程
SIGQUIT(Core)的默认处理动作是终止进程并且Core Dump
查看系统当中的系统资源:ulimit - a
云服务器下的核心转储是默认关闭的(why:每次运行程序挂掉都会在磁盘产生不小的core.pid文件)
我们把它打开:
再次运行程序向进程发出三号信号(Ctrl+\)
多产生一个core文件,5865是发生核心转储的进程ID
核心转储:
代码运行中出错时,将进程内存中的核心数据转储到磁盘上,生成core.pid文件
目的是为了调试定位问题
利用core文件进行事后调试除0异常
使用命令core-file core.pid 查看错误信息
可以看到被8号信号(SIGFPE)终止,15行报错
进程为什么会崩溃
程序崩溃的本质是收到了OS发送的信号
进程为什么会收到信号
当程序发生某种错误时,一定会在硬件层面上有所表现,进而被OS识别,向该进程发出信号
子进程收到8号信号,并且coredump为1,说明运行时程序崩溃时core dump了
ctrl+z(20号信号)暂停进程,把进程放到后台
jobs:查看后台进程
fg 序号 将后台恢复到前台
命令行参数:
将字符串转为int
可以看到通过命令行参数,在程序里面进行系统调用kill,依然产生了信号
abort函数使当前进程接收到信号而异常终止
特性:就像exit函数一样,abort函数总是会成功的,所以没有返回值
SIGPIPE是一种由软件条件产生的信号
在传入时间后,向进程发送14号信号
不像abort,捕获后并没有终止进程
每隔一秒打印递增的count:
每次alarm发出信号捕获后,有设置新的闹钟,就会一直打印
1.实际执行信号的处理动作称为信号递达
2.信号从产生到递达之间的状态,称为信号未决
3.进程可以选择阻塞某个信号
4.被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作
收到信号后:
所以在阻塞时(1到31号信号)收到多次该信号只处理一次
sigset_t(信号集)
作用:用于描述进程的block位图,pending位图的信号集
#include
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可以读取或更改进程的信号屏蔽字(阻塞信号集)
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里面,是输出型参数,方便恢复信号集
获取当前进程的未决信号集,通过set参数传出(输出型参数)。调用成功则返回0,出错则返回-1
示例:
可以看出:先屏蔽了2号信号,想进程发送2号信号不会被递达,pending对应位置修改为1
先阻塞了2号信号,发送二号信号,pending修改,count==6时恢复了2号信号,2号信号递达,执行捕捉代码,最后信号执行完,对应pending修改为0
上面提到进程收到信号之后,不是立即处理信号,而是在合适的时候
这个合适的时候就是内核态切换回用户态的时候
内核态通常执行OS代码,权限优先级非常高
用户态执行普通用户的代码的状态,受OS的管理
自定义处理函数状态切换4次
默认,忽略只切换了两次
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后
就会捕获信号退出循环
加入volatile后,不会将flag先置入寄存器,而是在读取内存中的值到寄存器中再检测
volatile的作用:保证了内存可见性