此地址观看更佳
异常控制流
控制转移
控制流
系统必须能对系统状态的变化做出反应,这些系统状态不是被内部程序变量捕获,也不一定和程序的执行相关。
现代系统通过使控制流 发生突变对这些情况做出反应。我们称这种突变为异常控制流
( Exceptional Control Flow,ECF
)
异常控制流
发生在系统的各个层次。
理解ECF
很重要
- 理解
ECF
将帮助你理解重要的系统概念。 - 理解
ECF
将帮助你理解应用程序如何与操作系统交互
- 通过陷阱(
trap
)或者系统调用(system call
)的ECF形式,向操作系统请求服务。
- 通过陷阱(
- 理解
ECF
将帮助你编写有趣的应用程序 - 理解
ECF
将帮助你理解并发 - 理解
ECF
将帮助你理解软件异常如何工作。
这一章你将理解如何与操作系统交互,这些交互都围绕ECF
8.1 异常
异常是异常控制流的一种,一部分由硬件实现,一部分由操作系统实现。
异常
(exception)就是控制流的突变,用来响应处理器状态的某些变化。状态变化又叫做
事件
(event)- 事件可能与当前执行指令有关
- 存储器缺页,算数溢出
- 除0
- 也可能与当前执行指令无关
- I/O请求
- 定时器产生信号
- 事件可能与当前执行指令有关
通过
异常表
(exception table)的跳转表,进行一个间接过程调用,到专门设计处理这种事件的操作系统子程序(异常处理程序
(exception handler))异常处理完成后,根据事件类型,会有三种情况
- 返回当前指令,即发生事件时的指令。
- 返回没有异常,所执行的下一条指令
- 终止被中断的程序
8.1.1 异常处理
为每个异常分配了一个非负的
异常号
(exception number)- 一些号码由处理器设计者分配
- 其他号码由操作系统内核的设计者分配。
系统启动时,操作系统分配和初始化一张称为
异常表
的跳转表。- 条目k包含异常k的处理程序的地址。
异常表
的地址放在叫异常表基址寄存器
的特殊CPU寄存器中。)异常
类似过程调用
,不过有以下不同- 过程调用,跳转到处理程序前,处理器将返回地址压入栈中。对于异常,返回地址是当前,或下一跳指令。
- 处理器会把额外的处理器状态压入栈中。
- 如果控制一个用户程序到内核,那么所有这些项目会被压入内核栈中,而是用户栈。
- 异常处理程序运行在内核模式下,这意味他们对所有系统资源有完整访问权限。
8.1.2 异常的类别
异常分为一下四类:中断
(interrupt),陷阱
(trap),故障
(fault)和终止
(abort)。
前者可以叫异步中断/异常或外中断 ,后三个可以叫同步中断/异常
中断
中断
是异步发生,是来自处理器外部的I/O设备的信号的结果。硬件中断不是由任何一条专门的指令造成,从这个意义上它是异步的。- 硬件中断的异常处理程序通常称为中断处理程序(interrupt handle)
- I/O设备通过向处理器芯片的一个引脚发信号,并将异常号放到系统总线上,以触发中断。
- 在当前指令执行完后,处理器注意到中断引脚的电压变化,从系统总线读取异常号,调用适当的中断处理程序。
- 当处理程序完成后,它将控制返回给下一条本来要执行的指令。
-
剩下的异常类型(陷阱,故障,终止)是同步发生,执行当前指令的结果。我们把这类指令叫做故障指令(faulting instruction).
陷阱和系统调用
陷阱
是有意的异常,是执行一个指令的结果。也会返回到下一跳本来要执行的指令。陷阱
最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用
- 用户程序经常需要向内核请求服务。
- 读文件(read)
- 创建进程(fork)
- 新的程序(execve)
- 终止当前进程(exit)
- 为了运行对这些内核服务的受控访问,处理器提供了一条特殊的
syscall n
的指令 - 系统调用是运行在内核模式下,而普通调用是用户模式下。
- 用户程序经常需要向内核请求服务。
故障
故障由错误引起,可能被故障处理程序修正。
- 如果能被修正,返回引起故障的指令。
- 否则返回
abort
例程,进行终结。
终止
- 终止是不可恢复的致命错误造成的结果,通常是一些硬件错误,比如DRAM和SRAM被损坏。
- 终止处理程序从不将控制返回给应用程序。返回一个
abort
例程。
8.1.3 Linux/IA32 系统中的异常
有高达256种不同的异常
- 0~31 由Intel架构师定义的异常,对任何IA32系统都一样。
23~255 对应操作系统定义的中断和陷阱。
- Linux/IA32 故障和终止
除法错误
- 一般保护故障
- 缺页
机器检查
- Linux/IA32 系统调用
在IA32系统中,系统调用是通过一条称为
int n
的陷阱指令完成,其中n可能是IA32异常表256个条目中任何一个索引,历史中,系统调用是通过异常128(0x80)提供的。C程序可用
syscall
函数来直接调用任何系统调用- 实际上没必要这么做
- C库提供了一套方便的包装函数。这些包装函数将参数打包到一起,以适当的系统调用号陷入内核,然后将系统调用的返回状态传递回调用函数。
- 我们将系统调用与他们相关联的包装函数称为系统级函数。
研究程序如何使用int指令直接调用Linux 系统调用是很有趣的。所有到Linux系统调用的参数都是通过通用寄存器而不是栈传递。
惯例
- %eax 包含系统调用号
- %ebx,%ecx,%edx,%esi,%edi,%ebp包含六个任意的参数。
- %esp不能使用,进入内核模式后,内核会覆盖它。
系统级函数写的hello world
int main() { write(1,"hello,world\n",13); exit(0); }
汇编写的hello world
string: "hello world\n" main: movl $4,%eax movl $1,%ebx movl $String,%ecx movl $len,%edx int $0x80 movl $1,%eax movl $0,%ebx int $0x80
8.2 进程
异常
是允许操作系统提供进程
的概念的基本构造快,进程
是计算机科学中最深刻,最成功的概念之一。
- 假象,觉得我们的程序是系统中唯一运行着的程序。我们的程序好像独占处理器和存储器。
- 这些假象都是通过进程概念提供给我们的。
进程
经典定义:一个执行中的程序实例.
- 系统中每个程序都是运行某个进程的
上下文
中的。
- 上下文是由程序正确运行所需的状态组成。
- 这个状态包括存储器中的代码和数据,它的栈,通用目的寄存器,程序计数器,环境变量等。
- 系统中每个程序都是运行某个进程的
进程
提供的假象
- 一个独立的
逻辑控制流
。 - 一个私有的
地址空间
。
- 一个独立的
8.2.1 逻辑控制流
- PC值的序列叫做
逻辑控制流
,或者简称逻辑流
8.2.2 并发流
逻辑流
也有不同的形式。- 异常处理程序,进程,信号处理程序,线程和Java进程都是逻辑流的例子。
一个逻辑流的执行在执行上与另一个流重叠,称为
并发流
,这两个流被称为并发地运行。- 更准确地说,流X和Y互相并发。
多个流并发执行的一般现象称为
并发
。- 一个进程和其他进程轮流执行的概念称为
多任务
。 - 一个进程执行它的控制流的一部分的每一时间段叫做
时间片
。 - 因此,多任务 又叫
时间分片
- 一个进程和其他进程轮流执行的概念称为
并发
的思想与流运行的处理器核数与计算机数无关。- 如果两个流在时间上重叠,即使运行在同一处理器,也是并发。
- 并行流是并发流的一个真子集。
- 两个流并发地运行在不同的处理器核或者计算机上,我们称为
并行流
。 - 它们并行地运行,且并行地执行
- 两个流并发地运行在不同的处理器核或者计算机上,我们称为
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持
并发