CSAPP学习笔记(异常控制流1)

时间:2021-06-11 14:28:07

1:诸如子进程结束之后父进程需要被告知,有时候应用程序需要系统调用,内核通过上下文切换将控制从一个进程切换到另一个进程,还有一个进程发送信号到另一个进程时接收者转而到它的信号处理函数去执行等等,我们的操作系统,甚至硬件无时无刻不在发生“突变”,我们把这种突变叫做异常控制流。即ECF(Exceptional Control Flow),它发生在计算机系统的各个层次。
2:为什么要理解异常控制流:
(1):ECF是操作系统实现I/O,虚拟存储,进程的基本机制。
(2):这将有助于你理解应用程序是怎样和操作系统交互的,实际上是通过“陷阱”或者叫“系统调用”的方式。比如写磁盘,从网络中读取数据,创建一个进程,等等
(3):操作系统为应用程序提供了强大的系统调用去使用,创建进程,通知其他进程系统,发送信号给其他进程等等,理解这些之后我们就不难写出有趣的程序了,例如一些web程序。
(4):理解ECF将使你真正的理解并发,还可以帮你理解软件是如何工作的,例如向setjmp和longjmp为什么可以实现跳转等等,也将会和你学到的C++的try函数以及catch函数加深理解。
3:异常
为什么要说异常,因为正常的很少,这个世界本来就是不正常的情况多,哪有那么多风平浪静的时候,扯得有点远。我们继续,异常的实现有硬件,有软件,假设现在有一个进程A正在运行,它突然需要调用read()函数从磁盘读取文件,此时由于read()函数属于系统调用,即要进入内核才可以使用的,所以当前的代码以及下面的代码就应该被保存下来,当然这是要进入内核态,我们就把遇到这个read()函数叫做“事件”,即它是一种状态的变化,我们的操作系统有一张叫做“异常表”的跳转表的东西,跳转到遇到这类信号应该去执行的代码的地方。
C++和java程序员会注意到术语“异常”也用来描述由C++和java以及catch,throw,和try语句形式提供的应用程序层的ECF,如果必须弄清楚,这就要牵扯到硬件和软件的不同了。
下面具体来描述下我们操作系统对于异常的处理:
在我们的系统中,每一个异常都对应一个特定的异常号,其中一些是硬件的设计者分配的,其他号码是由操作系统内核分配的,前者包括除0错,硬件设备异常,虚拟存储器缺页异常,存储器访问异常,等等,后者包括我们的系统调用和来自外部的I/O设备的信号。当操作系统启动的时候,内核会专门为我们分配并且初始化一张称为异常表的跳转表,其中按照不同的异常编号存放的是对应的处理异常的代码的地址。异常号是在异常表中的索引,异常表的起始地址存放在一个叫做异常表基址寄存器的特殊cpu寄存器里。
4:异常的种类:
一共有4种异常的种类:中断,陷阱和系统调用,故障,终止
中断:据说是异步的,但是这个异步怎么理解,苗帅学长这样解释:因为中断一般指的就是硬件中断,硬件发生中段的情况是随机的,是不为我们所知道的,所以这种情况我们称之为异步,这也就是中断,但是对于别的三种情况,诸如系统调用这是一种有意的异常,是执行一条指令的结果,并且我们知道它发生在了哪个地方。
陷阱和系统调用:可以说是一种有意的异常,只是我们异常处理的时候,会有内核为我们提供的通道去获得更大的权利,并且我们可以访问内核中的栈,它返回到我们的下一条指令。
故障:故障是由错误情况引起的,它可能能够被故障处理程序修正,如果能修正,返回到引起故障的指令,要是修复不了,那就返回到内核中的abort例程结束应用程序。
终止:终止是不可恢复的致命错误造成的结果,通常是一些硬件错误。直接到abort例程。
5:理解下并发的概念:
条件:如果流x和流y并发,那么满足情况有两种:
1:x在y开始之后并且y结束之前开始
2:y在x开始之后并且x结束之前开始
所以说并发是可以在单核CPU上跑的,不一定要多核,我觉得这以前是个误区。
6:用户模式和内核模式:
由于进程为我们每个程序提供了一种假象,即我们的每个程序都独占内存空间,并且看起来在某一时刻只有我们这一个程序在运行,为了将这种假象提供的完美,我们的处理器必需提供一种机制,限制一个应用可以执行的指令和它可以访问的存储器的位置,处理器通常是用某个控制寄存器的中的一个模式位来提供这种功能,没有设置模式位时,进程就运行在用户模式中,但是设置了模式位之后,我们的应用程序就获得了特权,进程就运行在了内核模式中,一个运行在内核模式中的进程可以执行指令集中的所有指令,访问所有存储位置。
linux为我们提供了一种聪明的机制:叫做“/proc”文件系统,它允许用户模式进程访问“内核数据结构的位置”,注意:访问的是位置,而不是内容。/proc文件系统将许多数据结构的内容输出为一个用户程序可以读的文本文件的层次结构。如:/proc/cupinfo
7:内核会为每一个进程维护一个上下文,上下文说的简单些就是进程目前的状态信息,我们通过保存这个上下文来达到我们能回到某个进程的某个运行状态,前提是发生过上下文切换之后。在进程执行的某个瞬间,内核有权利决定“抢占”当前的进程,并且通过上下文切换切换到另一个进程,这样可以减少资源的浪费(举一个例子:当某个应用调用read读取磁盘的时候,由于磁盘的读取速度是比较慢的,这时候内核可不会一直等几十毫秒,他自己会做决定切换上下文,可是凭什么切换呢?凭“时间片”,比如所有的系统都有某种产生周期性定时器中断的机制通常为1毫秒或者10毫秒),等到read()函数返回的时候,再将控制程序返回给调用read()函数的进程。