第一,我们能够识别这个信号。我们能知道这个信号是什么。
第二,我们能够在没有收到这个信号时,我们已经知道这个信号该怎么处理了。
那为什么这些信号那么进程它能够做到? 因为我们信号的处理能力,它属于我们进程内置功能的一部分。
第三,那么当一个进程呢收到一个具体信号时,进程呢它可能并不会立即处理信号,而是在合适的时候。
即信号具有一个时间窗口,进程具有临时保存信号的能力。
好比我们平时生活当中,你可能会收到各式各样的信号。但是当你收到了这种各式各样的信号之后呢,你当前可能正在忙着更重的事情。
因为信号的产生和你自己的运行本身是异步的呢当我们在进行我们正常的工作的时候呢,我们在收到信号之后并不会一直处理,那么在合适的时候处理。
第四,处理信号的情况也就三种。
1.一种是我们的默认动作,每种信号都有自己的默认处理方式,这个也就是属于内置的一部分。
2.一种是我们有些信号呢还可以用来被忽略。
3.一种是自定义动作。
所以说对信号的处理,你可以默认,可以忽略啊,也可以自定义。
第五,没有0号信号,1-31号信号是普通信号,31后面的是实时信号。
第六,9号和19号信号最特殊。不能捕捉,也不能屏蔽。
信号的产生有四大方式:键盘组合键,kill命令,系统调用,异常,软件条件
键盘组合呢,两个典型ctrl c,和ctrl z。
而键盘组合有个特点,那么如果我们要通过键盘给信号,给进程发信号,前提条件是你这个进程必须得是一个前台进程。
那么在windows当中一次登录那么就会给你分配一个终端,一个终端就会给你分配一个bash。
那么每一个终端啊或者每一次登录,它都允许你一个进程是前台进程,剩下的所有进程只能是后台。
前台进程呢它能够获取键盘锁定的数,而后台是不能的。那么最终前台进程和后台进程区分呢,主要是用来那么谁可以用来获取键盘数。
所以对我们来讲,整个我们一次登录过程之中,可以允许那么系统只允许一个前台进程。
那么但是可以允许多个后台进程,所以你的键盘呢在发送我们的信号时,只发送给前台进程。
shell是这个样子,当你一旦你登录一个终端之后呢,每一个终端都会配一个bash。
好,那么这个呢这个就是就是默认的前台进程,它获取我们和这个终端相关联的键盘数。
那么后来呢你自己启动一个进程,是默认启动的时候点前方运行,那么默认它就会变成前台进程,也就是bash会自动变成后台啊,那么这个前台进程呢,它当前呢就能够获取键盘输入了啊,打印消息也给你打到显示器上了。
比如做我们运行一个程序法打了个死循环啊,此时这个进程它就处于一种比如说失控状态,我们ctrl c就把这个进程杀掉了。
那么我们就是为什么ctrl c能够杀掉我们的前台进程呢?
这个问题它的前提条件,第一点是ctrl c是键盘输键盘输入给了前台进程。
【问题】
操作系统呢他怎么知道键盘上是有数据的?当你读写键盘的时候呢,你怎么知道用户把键盘键盘按下了呢?
读取键盘文件的本质,就是把键盘上面的数据拷贝到我们对应的缓冲区当中啊,系统你要把数据拷贝过来,你前提是你得知道键盘上有数据,你才能把数据拷贝过来。
在磁盘读写期间,磁盘呢就开始进行把数据给你从外设拷贝到内存了啊,那么设备和内存之间进行数据拷贝了,放到一半啊,可是这里关键的点在于,那么你怎么知道我们的数据被考完了呢?
那么你怎么知道网卡里有数据的呢?
键盘、网卡、键鼠,还有各种各样的这些设备,它都属于外设。操作系统它必须得知道每一种设备的随时随地的就绪状态,为什么呢?因为操作系统是硬件的管理者。
但是问题是操作系统怎么知道呢?这里呢其实是通过软硬件结合的方式来做的。
因为每一种外部硬件呢它都会在自己就绪了,被按下了,或者数据已经拷贝完成了的时候呢,那么通过我们对应的一些中断单元,向我们的c p u的针脚里去发送我们对应的这个硬件中断。
那么这个硬件中断的发送后呢,c p u呢就能获取到对应设备它的中断号。
我们c p u或者是操作系统拿到了这个中断号之后,此时c p u呢直接转而去执行我们对应的操作系统预先提供的一张方法叫做中断向量表。
中断向量表里面呢放的都是,可以理解成函函数指针。
所以它会指向我们操作系统当中的某个位置,然后呢对应的是操作系统的某个方法。
比如说这个它里面呢指向的是一个叫做读读键盘的方法啊,叫做读取键盘的方法。
然后根据你的终端号查中断向量表。执行表里的方法。
整个进行读取过程呢就跟个条件反射一样。硬件呢直接一中断,然后这个方法自动被调用
外设一旦就绪了,操作系统即便再忙,他也要在外设就绪的时候,由我们自己c p u来主动的那么帮帮我们去获取到我们的中断号,进而转而执行自己的代码。
当他执行中断向量表方法的时候,其实就是在执行操作系统的代码。
来一旦外设就绪,这个外设直接会给c p u发送中断。
那么中断之后我们的操作系统就知道了,知道之后操作系统就会根据中断号执行中断通道,把数据拷贝过来。操作系统呢,它会就是对你的键盘输入做识别。
你是普通输入,那我就让你输进来放到文件缓冲区里。如果你不是,你是什么卡住c这样的组合键就把你解释成信号了。
所以键盘这个设备是基于中断那么来进行工作的。
出现异常的本质就是发送信号。更深层次的本质是硬件异常。
出现异常不一定会让程序直接崩掉,因为我们可以利用信号捕捉自定义处理方法。自定义方法里可以让程序不退出。但这样又会出现问题:
一旦出异常了,杀不掉,你还你还在那疯狂占用c p u资源。
那么很显然就是让我们的程序就就就退不了,就有点问题啊。出现异常之后,捕捉之后,它会出现类似于死循环那种现象。
【问题】操作系统是如何知道程序中出现除0错误,或者野指针这样的错误的呢?
c p u内部有一个状态寄存器,而状态寄存器里面有对应的一个我们称之为叫做比特位啊或者状态标志位,我们称之为溢出标志位。
好,当你做除零的时候呢,在c p u当中呢它会减它会直接除零了嘛。
那不就是越界了嘛,就相当于直接一除,数字非常大了。那除以无穷无穷小的数,这个结果一定是非常大的。所以它的计算直接就溢出了,而溢出标志位就由零表示无效改为有效了。
一旦我们当前进程在执行的时候,对应的溢出标志位一旦除零了,溢出标志位就由零变一了。
那么这是在状态寄存器硬件上直接会表现出来的。
说你c p u的寄存器硬件你只有一个。
但对不起,我对应的c p u寄存器里的数据可以有无数份。
所以硬件只有一套,但是我们进程它在被调度期间,那么c p u寄存器里放的都属于当前进程的上下文。
那么当我们对应的c p u在进行我们对应的调度执行的时候,那么一旦它出现异常了,那么对应的状态寄存器直接由零至一了。
我们识别到状态寄存器出异常了,也就是说该进程是否出异常与进程切换无关。当前进程在运行时出异常,他改的是自己的状态寄存器里面的内容。
当该当当前进程在被调度的时候,那么你要把自己的硬件上下文带走,然后别人要把别人的硬件上下文再拿回来。
所以进程之间是不会影响的。
所以即便你修改了c p u的硬件,这个寄存器放在寄存器里的内容,那么你不会影响其他进程
c p u它也是硬件,所以我们对应的那么操作系统,它要对c p u的健康状态做处理。
很显然一个进程出异常就不让不要让他往往后跑了。
现操作系统发现这个c p u就越越界了,那么怎么办?
然后才有了操作系统直接向进程发送信号,然后你这个进程收到信号,你自己崩溃吧。最终这个除零的问题会被转化成硬件问题,会表现在硬件上,进而被操作系统识别了。
所以对我们来讲,为什么刚刚会死循环呢?
好,因为自始至终你引发了硬件问题,但你没有修正过硬件问题。
所以硬件异常一直存在,硬件异常一直存在,随着你的调度上下文再怎么被保护,对不起。
上下文错误一直存在,所以c p u操作系统就一直报错。操作系统一直检测到你对应的有这个异常,所以操作系统当然一直在给你发信号,你呢就一直在那里打印我们对应的信号捕捉方法。
你可以把它捕捉了嘛,捕捉了它一直被调度,但硬件问题一直存在,所以这个问题一直在大数据化。
好,那么同学们其实我刚为什么要说到你们在家里的异常,包括家里的异常呢?
其实我最终就想告诉大家,当你听到了异常的时候,你不要指望着你能修复这个异常啊,大部分情况下你是无法修复的。
好,那除非语言层给你提供了其他机制,人家给你提供抛就是异常处理呢,仅仅是为了让你在一个统一的地方来那么做一下异常的后续工作,不是为了让你解决的。
异常只会有硬件产生吗?
不只是硬件出错会发生异常,软件也会发生异常。
管道中一直在进行写入,而读端把对应的读文件描述符直接关。即把读端还关了,那么写端一直在写。
操作系统就要把你的写段直接干掉。不光干掉还会发送信号。
今天写入时是操作系统不能让你写了,因为操作系统检测我申请的管道这玩意不存在了,这就是最典型的我们对应的叫做软件问题引起的异常。
所以异常可以由我们对应的软件产生,软件上呢不仅仅是可以出异常,它也可以再出一些叫做特殊事件。
啊,我们把这种特殊事件我们称之为软件条件。
比如说我们接下来要谈的一种特殊事件,叫做我们对应的叫做闹钟。
那么一旦闹可以你的进程可以向系统里设定闹钟,闹钟响了就可以给你的进程。
触发对应的条件,执行对应的动作,这个呢我们就称之为软件条件。
我们在进行我们对应的信号处理的时候,当我们在while循环的时候啊,我们的执行。
我在执行我的代码是这个信号可能随时随地都可能产生。
那么这种过程就叫做信号的产生,和我自己代码的运行是异步的。就是大家在执行的时候互相不会影响你跑你的,我跑我的,只是在某一个时间点大家产生了交集,仅此而已。
信号的产生有多种方式,但最终都是借助操作系统发送信号的,也就是信号是由操作系统发送的。
一旦信号产生,操作系统就会将信号发送给进程的PCB。
而进程是如何知道自己接收还是没有接收到信号的呢?
每个PCB里都会有一个信号管理位图,有32个比特位,用来代表1-31号信号,如果出现信号,有就将对应比特位置1,没有就置0.所以所谓的发信号,本质就是操作系统修改PCB里的信号位图里的比特位。将比特位置1.就代表发送信号。