深入理解计算机系统——异常控制流学习笔记
异常
-
异常就是控制流中的突变,用来响应处理器状态中的某些变化。
-
处理完异常后,有三种结果:
- 将控制交给发生异常前的指令
- 将控制交给将执行的吓一条指令
- 终止被中断的程序
异常处理
-
当操作系统启动时会生成一张异常表,存放着每一种异常处理程序的地址。
-
当处理器触发异常后,会拿异常号去异常表中查找生成异常处理程序的地址。
异常表的起始地址放在异常表基址寄存器中。
-
过程调用时,在跳转到处理程序之前,处理器将返回地址压入栈中。
异常的类别
- 异常分为四类:
-
中断
-
中断是异步发生的,来自处理器外部的I/O设备的信号的结果,不是执行一条指令触发的。
-
中断处理:
-
-
陷阱和系统调用
-
陷阱是有意的异常,是执行一条指令的结果。
-
当用户程序需要向内核请求服务时,使用陷阱异常来做到系统调用,比如说读一个文件、创建一个新的进程、加载一个新的程序等。
-
系统调用和普通函数的调用看起来一样,但是它们运行的模式不同:
- 普通函数运行在用户模式下:限制了函数可以执行的指令的类型,而且只能访问与调用函数相同的栈。
- 系统调用运行在内核模式下:允许执行特权指令,并访问定义在内核中的栈。
-
-
故障
- 故障是由程序错误引起的。比如说缺页异常——虚拟地址对应的物理页面不存在时。
- 故障是由程序错误引起的。比如说缺页异常——虚拟地址对应的物理页面不存在时。
-
终止
- 终止是发生不可恢复的致命错误引起的,通常是一些硬件错误。
- 终止是发生不可恢复的致命错误引起的,通常是一些硬件错误。
进程
-
进程的经典定义:一个执行中程序的实例。
-
系统中每个程序都运行在某个进程的上下文中。
上下文有程序正确运行所需要的状态组成。
这个状态包括:存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。
-
每个进程看起来好像都有一个独立的逻辑控制流,一个私有的地址空间。
逻辑控制流
-
逻辑控制流:程序在执行过程中的一系列的程序计数器(PC)的值。
并发流
-
一个进程的逻辑控制流在未结束时,另一个进程的逻辑控制流启动,这两个逻辑流就称为并发流。
-
多个流并发的执行叫做并发。
一个进程和其他进程轮流运行的概念称为多任务。
一个进程执行它的控制流的一部分的每一时间段叫做时间片。因此多任务叫做时间分片。
-
两个并发流运行在不同的处理器上叫做并行流。并行流是并发流的真子集。
私有地址空间
-
在一台n位地址的机器上,地址空间是2的n次方个可能地址的集合。
-
进程为每个程序提供它自己的私有地址空间,和这个空间中某个地址相关联的那个内存直接是不能被其他进程读或者写的,从这个意义上说,这个地址空间是私有的。
-
X86-64的地址空间:
用户模式和内核模式
- 用户模式和内核模式执行指令的权利不同,用户模式只有通过异常进入内核模式。
上下文切换
- 通过上下文切换实现多任务。
- 上下文就是内核重新启动一个被抢占的进程所需的状态。
- 执行上下文的决策叫做调度:
- 保存当前进程的上下文
- 恢复某个先前被抢占的进程被保存的上下文
- 将控制传递给这个新恢复的进程。
- 进程A执行一个读文件的上下文切换如下:
信号
-
一个信号就是一条消息,它通知进程系统中发生了一个某种类型的事件。
-
信号序号与名称以及相应事件和默认行为如下:
信号术语
传送一个信号到目的进程有两个步骤组成:
-
发送信号:内核通过更新目的进程上下文中的某个状态,发送一个信号给目的进程。
发送信号有两种原因:
- 内核检测到一个系统事件,比如除零错误或者子进程终止。
- 一个进程调用了KILL函数。
-
接收信号:当目的进程接收到信号后就会触发控制转移到信号处理程序。
- 一个没有被目的进程接收的信号叫做待处理信号。在任何时刻,一种类型最多只能有一个待处理信号,当有待处理信号时,接下来所有该类型的发送信号都将被丢弃。
发送信号
- 进程组
- 每个进程都只属于一个进程组,信号发送是基于进程组的。
- 默认父进程和子进程同属于一个进程组。
- 用/bin/kill程序发送信号
- 如
kill -9 15213
,发送9(SIGKILL)给进程15213
- 从键盘发送信号
-
Unix shell 使用作业(job)的抽象概念来表示为对一条命令行求值而创建的进程。
-
在任何时刻最多只有一个前台作业和多个后台作业。
如:
linux> ls | sort
,会创建一个前台进程组和两个后台进程组: -
在键盘上输入Ctrl + C 就是发送一个SIGINT信号到前台进程组中的每个进程来终止前台作业。
- 用kill函数发送信号
- 进程通过调用kill函数发送信号给其他进程(包括它们自己)
- 用alarm函数发送信号
-
进程可以通过调用alarm函数向它自己发送SIGALARM信号:
返回:前一次闹钟剩余的秒数,若以前没有设定闹钟,则为0.
信号接收
-
每个信号类型都有一个预定义的默认行为:
- 进程终止
- 进程终止并转储内存
- 进程停止(挂起)直到被SIGCONT信号重启
- 进程忽略该信号
-
进程可以通过使用signal函数修改和信号相关联的默认行为
-
信号处理程序可以被其他信号处理程序中断:
阻塞和接触阻塞信号
-
隐式阻塞机制:内核默认阻塞任何当前处理程序正在处理信号类型的待处理的信号。
信号重启- 进程忽略该信号
-
进程可以通过使用signal函数修改和信号相关联的默认行为
-
信号处理程序可以被其他信号处理程序中断: