信号的产生

1.中断
中断是计算机系统中的一种机制,用于在CPU执行指令的过程中暂停当前任务,转而执行特定的处理程序(称为中断处理程序或中断服务程序),以响应硬件或软件产生的事件或条件
外设是可以间接地向CPU特定的帧脚发送就绪信息的
把特定帧脚的编号称为中断号
中断号是一个整数值,用于标识系统中不同的中断类型或事件。当一个外设发生中断时,它会向 CPU 发送一个中断号,CPU 根据这个中断号来确定要执行的中断服务程序。中断服务程序是事先定义好的,用于处理特定类型的中断事件。
通常是由操作系统内核负责创建和管理中断向量表。中断向量表是一个数据结构,通常可以视为函数指针数组。用于将中断号映射到相应的中断处理程序或中断服务例程。
在操作系统启动时,内核会负责初始化中断向量表
2.信号的产生
①.通过终端按键产生信号
ctrl+c 向前台发送2号信号,通常用于请求中断前台进程的执行。当用户按下Ctrl+C时,操作系统会向当前前台进程发送SIGINT信号,这通常导致程序中断执行并退出。
ctrl+* SIGQUIT信号是一个终止信号,它会导致进程终止并且会生成一个核心转储文件,默认处理动作是终止进程并生成core dump*
core dump
当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core,这叫做core dump.进程异常终止通常是因为有bug,比如非法内存访问导致段错误,时候可以用调试器检查core文件以查清错误原因.这叫做post-mortem debug(事后调试).一个进程允许产生多大的core文件取决于这个进程的resource limit(这个信息保存在PCB中),默认是不允许产生core文件的,因为core文件可能包含用户密码等敏感信息,不安全,在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文件。
$ulimit -c 1024
②.调用系统调用向进程发信号

#include<signal.h>
int kill(pid_t pid,int signo);//给某个进程发送任意信号
int raise(int signo);//给自己发送任意信号
//成功返回0,错误返回-1


```cpp
#include<stdlib.h>
void abort(void);
//就像exit函数一样,abort函数总是会成功的,所以没有返回值

③.硬件异常产生信号
<1>除0操作导致的硬件异常:
在这里插入图片描述
在这段代码中,有除0操作,我们知道,除0得到的是无穷大的数,所以在编程的时候是不允许出现的。
在运行的时候,直接出错,没有再执行下去,是因为接收到了信号。
接收到的信号是SIGFPE信号,编号为8号。
这其实就是一种硬件异常产生的信号
在这里插入图片描述
CPU中有很多的寄存器,例如eax,ebx,eip等等。
CPU会从内存中将代码中的变量拿到寄存器中进行运算,如果有必要,还会将运算的结果放回到内存中。
还有一个状态寄存器,如果CPU在运算的时候发现了除0操作,就会将状态寄存器的溢出标志位置一。
此时就意味着硬件产生了异常。而操作系统是一个进行软硬件资源管理的软件,CPU的中状态寄存器的溢出标志位置一后,操作系统可以第一时间拿到。
除0导致硬件异常以后,操作系统会给对应的进程发送SIGFPE信号。
当进程接收到SIGFPE信号以后,默认的处理方式就是结束进程。
现在我们对这个SIGFPE信号注册一个自定义处理方式:
在这里插入图片描述
在这里插入图片描述
进程收到信号后进程不退出,随着CPU时间片的轮转就会再次被调到。
CPU中只有一份寄存器,但是寄存器中的内容属于当前进程的上下文。
当进程被切换的时候,就有无数次的状态寄存器被保存和恢复的过程。
而除0操作导致的溢出标志位置一的数据还会被恢复到CPU中。
所以每一次恢复的时候,操作系统就会识别到,并且给对应进程发送SIGFPE信号。
所以就会导致上面不停调用自定义处理函数,不停打印接收到的信号编号。

<2>解引用空指针导致的硬件异常:
在这里插入图片描述
上面代码中存在对空指针的解引用操作,空指针的本质是(void*)0,而0地址处是不允许我们用户进行访问的,这部分属于内核空间。
在这里插入图片描述
运行的时候直接出错,没有再运行下去,也是因为接收到了信号。
接收到的信号是SIGSEGV,编号是11。
这同样是一种硬件异常产生的信号。
在这里插入图片描述
我们之前一直谈论的页表时间上是页表+MMU,而MMU是在CPU中的,未来简便,我们就只说页表。
进程地址空间和物理内存之间的映射关系实际上是有MMU去完成映射的。
当对空指针解引用的时候,MMU会拒绝这种操作,从而产生异常标志。
操作系统拿到MMU产生的异常以后就会给对应的进程发送SIGSEGV信号。
当进程接收到编号为11的SIGSEGV信号以后,默认的处理动作就是结束进程。
在这里插入图片描述

硬件异常所产生的信号,如果不结束这个进程,我们是没有能力去处理这个进程的。
随着时间片的轮转,这个导致硬件异常的进程还会不停的调到,所以操作系统会不停的向进程发送信号。
硬件异常产生的信号并不会显示发送,而是由操作系统自动发送的。

④.由软件条件产生信号
<1>:读端关闭触发的信号:
比如在学习匿名管道的时候,当读端关闭的时候,写端所在进程就会收到编号为13的SIGPIPE信号结束进程。
在这里插入图片描述
读端5秒钟之后关闭,写端进程注册自定义处理方式,打印写端接收到的信号编号,但是不结束进程。
在这里插入图片描述

<2>:闹钟触发的信号

#include<unistd.h>
unsigned int alarm(unsigned int seconds)

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发送SIGALRM信号,该信号的默认处理动作是终止当前进程
这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数

闹钟的管理:

操作系统中会有很多个进程,我们可以创建一个闹钟,那么其他进程也可以创建闹钟,这样就会存在很多个闹钟,那么这些闹钟是怎么管理的呢?先描述再组织。

首先需要创建一个闹钟的结构体,伪代码:

struct alarm
{
	unit64_t when;//定时时长
	int type;//闹钟类型,一次性还是周期性
	task_struct* p;//所属进程的地址
	struct_alarm* next;//下一个闹钟的地址
	//其他属性
}

大概就是这样的一个结构体来描述闹钟,必须由的肯定是定时时长,所属进程。
接下来就是组织了,用某一种数据结构来管理这些闹钟对象,为了方便管理,可以选择优先级队列prority_queuq来管理。
在这里插入图片描述
将定时时间最小的闹钟放在前面,时间长的放在后面。
操作系统每次只需要检测队首的定时时间是否达到就可以。
达到了就向对应进程发送SIGALRM信号,并且从队列中取出,以待再次检测。
操作系统会周期性的检测链表中的这些闹钟

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值