1.信号的概念:
在生活中的信号:红绿灯,下课铃声,闹钟
红绿灯,你为什么认识红绿灯?
1.能识别红绿灯
2.能理解红灯绿灯黄灯
也就是说信号在还没产生的时候,我们已经认识信号并知道如何去处理
信号产生了,我们并不一定要立即处理,而是选择合适的时间去处理,这也就意味着我们有暂时保存信号的能力
信号的到来的时候我们并不清楚,信号的到来相对于我们正在做的工作,是异步产生的
同步与异步:老师在教室上课,结果忘记带鼠标,于是叫小王去办公室拿鼠标,老师一直等待小王拿回鼠标才开始上课,这叫同步,而老师并没有等小王,而是直接上课,这叫异步
什么叫信号?
信号是一种向目标进程发送通知消息的机制
0号信号?
代表没有收到信号
2.信号的产生:
进程的两种运行模式,1.前台 2.后台(./xxx &) jobs:查看后台任务
前台只能有一个进程,后台可以有多个(fg将后台转到前台) ctrl+c 终止前台进程 ctrl+z:将前台运行不能被暂停,该前台立即被放到后台(前台变后台) bg:前台到后台
操作系统怎么知道有数据从键盘上输入了?中断;存在函数指针数组叫做中断向量表
信号本质是用软件来模拟上述中断的行为
产生的几种方式
.键盘
ctrl +c=signal 2 向前台发送2号信号
ctrl z (暂停) , ctrl \(终止进程)
.系统调用kill
给自己发送信号
给自己发送SIGABRT信号,进程直接终止
自定义了SIGABRT信号,为什么进程没有退出?
abort()函数不会被更改
.异常(CPU->OS->进程)
int a=1;a/=0; 发生SIGFPE-8信号
为什么一直在处理8号信号?
进程并没有退出,异常一直存在,但是进程没有退出,依旧要去处理异常
那么后续代码还会执行吗?
不会
硬件报错
在硬件上出错的,CPU上的报错会传递给OS,OS解释为kill命令,给目标进程发送信号
无论有多少中产生方式,最终都是OS向进程发信号
OS如何发送信号给进程,进程又是如何表示自己收到了信号?
OS发信号本质是朝进程位图中去写信号,进程中维护了位图,表示自己收到的信号。每个进程都有一张自己的函数指针数组,数组下标对应了信号编号,再根据位图的内容去处理信号。
.软件条件
管道为软件资源,管道的读端关闭,写段未关闭,OS会检查管道的一些字段去给管道发送SIGPIPE,关闭管道
闹钟 alarm SIGALRM
return value:返回剩余时间
操作系统中的时间
1.操作系统只要将进程调度号就能完成用户的操作
2.操作系统本质上时死循环
3.信号的保存
a.实际执行信号的处理动作称为信号递达(Delivery)
b.信号从产生到递达之间的状态,称为信号未决(Pending)。此时信号存储在未决表中
c.进程可以选择阻塞 (Block )某个信号。未决之后,暂时不递达(处理),会一直储存在未决表里
d.被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.我们可以提前阻塞信号或解除阻塞信号
e.注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
信号集操作函数:
int sigemptyset(sigset_t *set); 全部清空
int sigfillset(sigset_t *set); 全部填充
int sigaddset (sigset_t *set, int signo); 添加某个信号
int sigdelset(sigset_t *set, int signo); 删除某个信号
int sigismember(const sigset_t *set, int signo); 判定信号是否在集合里
修改block表
sigprocmask
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
how:SIG_BLCOK;SIG_UNBLOCK;SIG_SETMASK
SIG_BLOCK:新增一些屏蔽 mask=mask|set
SIG_UNBLCOK:解除一些屏蔽 mask&=~mask
SIG_SETMASK:重新设计屏蔽 mask=set
oset:输出型参数,返回老的BLOCK位图
是否屏蔽了所有信号,进程就杀不死? 9号信号无法被屏蔽
修改pending表
sigpending
set:输出型参数
一开始先屏蔽二号信号,但此时未接收到2号信号,所以全为0
发送二号信号此时出现1;
清除Block表后,未决表中的信号递达,处理信号
4.信号的处理
在合适的时候处理,是什么时候处理?
在执行默认方法从用户态转换成内核态,执行信号检测和信号处理
在执行自定义方法时,进程在在内核态中保存上下文,然后返回到用户态,执行用户的自定义方法,在通过sigreturn接口返回到内核态,再次返回到用户态上次未执行的代码继续执行,这个过程也叫做信号的捕捉
用户态是一种受控的状态,他访问的资源是有限的
内核态是一种操作系统工作的状态,能够访问大部分资源
用户态只能访问自己进程地址空间内的0-3GB空间,而内核态可以让用户以OS的身份访问3-4GB,此时跳转到内核地址空间,通过内核级页表访问物理内存中的OS代码
所以无论如何调度,CPU都能直接找到OS,我们所有的代码执行都能在自己的进程地址空间内通过跳转的方式进行调用和返回
CPU是如何表示这两种状态的?
CPU内会有CS寄存器,其中有2个比特位,1表示内核,3表示内核
1.默认行为 man 7 signal SIG_DFL signal(2,SIG_DFL)
2.忽略 SIG_IGN signal(2,SIG_IGN)
3.自定义 signal
-
特例:9号信号不能被自定义
5.信号的其他补充问题
Core Down
当进程崩溃时,将所有的核心信息转储到磁盘中,由pid+core命名
如何进行自定义信号的捕捉
sigaction
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
Linux不允许正在处理一个信号时,又收到另一个同种信号,此时,OS在处理这个信号的同时将2号信号阻塞住,直到处理完成后恢复Block表,如果除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字
sigemptyset(act.mask)
sigaddset(act.mask,3)