信息安全系统设计基础第十一周 20135218 姬梦馨
第8章 异常控制流
控制流:控制转移序列。
控制转移:从一条指令到下一条指令。例:从k到k+1的过渡称为控制转移。
异常控制流:现代操作系统通过使控制流发生突变来对系统状态做出反应,这些突变称为异常控制流。
平滑:指在存储器中指令都是相邻的。
突变:出现不相邻,通常由诸如跳转、调用、和返回等指令造成。
8.1 异常
异常是ECF的一种,一部分由硬件实现,一部分由操作系统实现。就是位于硬件和操作系统之间的ECF。
硬件上,系统状态实际是处理器的状态,处理器的状态通常就是不同的位和信号(寄存器的位),处理器状态的变化(比如说某个bit置一)称为事件。
出现异常的处理方式:
1.处理器检测到有异常发生
2.通过异常表,进行间接过程调用,到达异常处理程序
3.完成处理后:①返回给当前指令②返回给下一条指令③终止
异常的类别
异常的类别——中断、陷阱、故障和终止
1 中断处理:异步是指硬件中断不是由任何一条指令造成的,而是由外部I/O设备的事件造成的。
中断处理程序——异步异常——由处理器外部I/O设备中的事件产生的。同步异常是执行一条指令的直接产物。
2 陷阱和系统调用:系统调用是一些封装好的函数,内部通过指令int n实现。
3 陷阱最重要的用途是提供系统调用。系统调用运行在内核模式中,并且可以访问内核中的栈。
陷阱最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用。
4 系统调用的参数是通过通用寄存器而不是栈来传递的,如,%eax存储系统调用号,%ebx,%ecx,%edx,%esi,%edi,%ebp最多存储六个参数,%esp不能用,因为进入内核模式后,会覆盖掉它。
5 故障
6 一个经典的的故障示例是缺页异常,当指令引用一个虚拟地址,而该虚拟地址相对应的物理页面不在存储器中,因此必须从磁盘中取出时,就会发生故障。
7 终止
8 终止是不可恢复的致命错误造成的结果,通常是一些硬件错误,比如DRAM或者SRAM位被损坏时发生的奇偶错误。终止处理程序从不将控制返回给应用程序。处理程序将控制返回给一个abort例程,该例程会终止这个应用程序。
系统中每种类型的异常都分配了一个唯一的非负整数的异常号,一些是处理器设计者分配的:零除、缺页、存储器访问违例、断点、算术溢出
一些是操作系统内核设计者分配的:系统调用、来自外部I/O设备的信号。
我们将系统调用和与它们相关联的包装函数称为系统级函数。8.2 进程
异常是允许操作系统提供进程的概念所需要的基本构造块。
进程(操作系统层):逻辑控制流,私有地址空间,多任务,并发,并行,上下文,上下文切换,调度。
进程提供给应用程序的关键抽象:
一个独立的逻辑控制流,它提供了一个假象,好像我们的程序独占的使用处理器。
一个私有的地址空间,它提供了一个假象,好像我们的程序独占的使用存储器系统。
上下文是集合,进程其实是这个集合下实际一条一条执行代码的过程。进程包含上下文以及执行的过程。
PC值的序列称为逻辑控制流。
并发流:
1 并发流:并发流一个逻辑流的执行在时间上与另一个流重叠,叫做并行流
2 并发:多个流并发执行的一般现象称为并发。
3 多任务:多个进程并发叫做多任务。
4 并行:并发流在不同的cpu或计算机上,叫做并行。
多个流并发的执行的一般现象称为并发。
一个进程和其他进程轮流运行的概念称为多任务。
x86 linux地址空间顶部是保留给内核的:代码、数据、
栈。
操作系统内核使用一种较高层次的异常控制流来实现多任务。这种异常控制流称为上下文切换。
上下文切换是建立在前面的4种异常的基础上的。
内核为每个进程维持一个上下文,上下文就是内核重新启动一个被抢占的进程所需的状态。
在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程。这种决定就叫做调度,是由内核中称为调度器的代码处理的。
上下文切换:
保存当前进程的上下文。
恢复某个先前被抢占的进程被保存的上下文。
将控制传递给这个新恢复的进程。
8.3 系统调用错误
1:当UNIX系统级函数遇到错误时,它们典型地会返回-1,并设置全局整数变量errno来表示什么出错了。
2:通过使用错误处理包装函数,我们可以更进一步地简化我们的代码。对于一个给定的基本函数foo,我们定义一个具有相同参数的包装函数Foo,但是第一个字母大写了。包装函数调用基本函数,检查错误,如果有任何问题就终止。
8.4 进程控制
进程总处于下面三种状态之一:
运行——在cpu上运行,或者,等待运行且最终会运行(会被内核调度)
停止——进程被挂起(也就是被其他的进程抢占了),且不会被调度,但可以被信号唤醒
终止——进程被永远的停止了,受到终止信号,或者从主程序返回,或者调用exit函数。
进程会因为三种原因终止进程:收到信号,该信号默认终止进程;从主程序返回;调用exit函数。
过程包括:
获取进程id
创建和终止进程
回收子进程
让进程休眠
加载并运行程序
回收过程中:
1 回收:当一个进程终止时,内核并不立即把它从系统中清除。相反,进程被保持在一种已终止的状态中,直到被它的父进程回收。
2 僵死进程:一个终止了但是还未被回收的进程称为僵死进程。
3 回收子进程的两种方法:1,内核的init进程 2,父进程waitpid函数
4 waitpid函数有点复杂,默认地(当options=0时),waitpid挂起调用进程的执行,知道它的等待集合中的一个子进程终止。
让进程休眠:
1 sleep函数将一个进程挂起一段指定的时间。
2 如果请求的时间量已经到了,sleep返回0,否则返回还剩下的要休眠的秒数。后一种情况是可能的,如果因为sleep函数被一个信号中断而过早地返回。我们将在8.5节中详细讨论信号
3 pause函数让调用函数休眠,直到该进程收到一个信号。
fork函数的三个参数:pid、
status、
options。
wait函数等价于waitpid(-1, &status, 0)。
execve函数在当前进程的上下文中加载并运行一个新程序。fork一次调用两次返回,execve调用一次,从不返回。
fork函数和execve函数的区别:
fork函数在新的子进程中运行相同的程序,新的子进程是父进程的一个复制品。
execve函数在当前进程的上下文中加载并运行一个新的程序,它会覆盖当前进程的地址空间,但并没有创建一个新进程。
新的程序仍然有相同的pid,并且继承了调用execve函数时已打开的所有文件描述符。
8.5 信号
底层的硬件异常是由内核异常处理程序处理的,
发送信号——内核通过更新目的进程上下文中的某个状态,告诉目的进程,有一个信号来了。
接受信号——当目的进程被内核强迫以某种方式对信号的发送做出反应时,目的进程就接收了信号。
发送信号的方式/bin/kill、
键盘发送信号、
kill函数、
alarm函数。
接收信号:
1.忽略 2.终止 3.执行信号处理程序,捕获信号
每个信号类型都有一个预定的默认行为:
(1)进程终止
(2)进程终止并转储存储器
(3)进程停止直到被SIGCONT型号重启
(4)进程忽略该信号
信号处理:
待处理信号被阻塞。Unix信号处理程序通常会阻塞当前处理程序正在处理的类型的待处理信号。
待处理信号不会排队等待。任意类型至多只有一个待处理信号。因此,如果有两个类型为K的信号传送到一个目的进程,而由于目的进程当前正在执行信号K的处理程序,所以信号K时阻塞的,那么第二和信号就简单地被简单的丢弃,他不会排队等待。
系统调用可以被中断。像read、wait和accept这样的系统调用潜在地会阻塞进程一段较长的时间,称为慢速系统调用。在某些系统中,当处理程序捕获到一个信号时,被中断的慢速系统调用在信号处理程序返回时不再继续,而是立即返回给用户一个错误的条件,并将errno设置为EINTR。
三种方法
-
执行默认操作
-
忽略信号
-
捕捉信号:执行信号处理函数,切换到用户态。
捕捉:signal函数。
8.6 非本地跳转
c语言提供一种用户级异常控制流形式——非本地跳转。
c语言提供了一种用户级异常控制流形式,称为本地跳转。通过setjmp和longjmp函数来提供。
setjmp函数只被调用一次,但返回多次:一次是当第一次调用setjmp,而调用环境保存在缓冲区env中时,
一次是为每个相应的longjmp调用。另一方面,longjmp只调用一次,但从不返回。
8.7 操作进程的工具
- STRACE:打印一个正在运行的程序和它的子进程调用的每个系统调用的轨迹。对
- PS:列出当前系统中的进程(包括僵死进程)
- TOP:打印出关于当前进程资源使用的信息。
- PMAP:显示进程的存储器映射。proc:一个虚拟文件系统,以ASCII文本格式输出大量内核数数据结构的内容,用户程序可 cat 2 / proc / load avg” , 观察在Linux系统上的平均负载。
参考资料
课本第八章,其中截图均来自《深入理解计算机系统》pdf版。