操作系统-中断异常机制

操作系统运行环境与运行机制

1
2
3
4
5
操作系统运行环境
CPU状态
中断/异常机制
运行机制
系统调用
1
2
3
4
5
6
7
8
中央处理器(CPU): 运算器,控制器,一系列寄存器 以及 高速缓存 构成
两类寄存器:
用户可见寄存器: 高级语言编译器分配使用,减少程序访问内存次数
控制和状态寄存器: 控制处理器的操作,操作系统代码使用
常见控制状态寄存器:
程序计数器(PC program counter):记录将要取出指令的地址
指令寄存器(IR instruction register): 记录最近取出的指令
程序状态字(PSW program status word) : 记录处理器的运行状态,如条件码,模式,控制位等信息

操作系统需求 – 保护

1
2
3
4
5
6
从特征考虑: 并发,共享
提出要求: 保护和控制

硬件提供基本运行机制:
处理器具有特权级别,能在不同的特权级别下运行不同的指令集合
硬件机制可将 OS 和 用户程序隔离

处理器状态(模式)

1
2
现代处理器,cpu状态分为 二,三,或四种
在 程序状态字寄存器中,专门设置一位,根据运行程序 对资源和指令的使用权限,设置不同的CPU状态

操作系统需要两种 CPU状态

1
2
3
4
5
内核态(kernel mode): 运行操作系统程序   
用户态(User mode): 运行用户程序

特权指令 : 只能操作系统使用
非特权指令 : 用户程序可以使用的指令 (操作系统也能使用)

例子: x86系列处理器

1
2
3
4
5
6
7
支持 4 个特权级别, 称为特权环: R0, R1, R2, R3
R0相当于 内核态
R3相当于 用户态
R1,R2 介于之间
不同级别能运行的指令集合不同

目前大部分基于x86的操作系统都只使用了 R0,R3 两个级别

CPU状态之间的转换

1
2
3
4
5
6
7
8
9
用户态 -》 内核态
唯一途径: 中断/异常/陷入机制

内核态 -》 用户态
设置程序状态字PSW

一条特殊的指令: 陷入指令 (又称 访管指令, 用户态访问管理态的指令)
提供给用户程序的接口,用户调用操作系统的服务
例如: int, trap, syscall, sysenter/sysexit

中断与异常机制

  • 介绍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
重要性: 好比汽车的发动机
可以说: 操作系统是 由 "中断驱动" 或 "事件驱动" 的
主要作用:
处理设备发来的中断请求
捕获用户程序提出的服务请求
防止用户程序执行过程中的破坏性活动
等等

概念:
CPU对系统发送的某个事件做成的一种反应 (事件的发送改变处理器的控制流)
CPU暂停正在执行的程序, 保留现场,去执行相应的事件处理程序,处理完成后返回断点继续执行

特点:
随机发送
自动处理
可恢复

中断的引入:
支持CPU和设备之间的并行操作
异常的引入:
CPU执行指令时本身出现的问题 (错误异常处理程序,系统调用)

事件:
中断(外中断):I/O中断,时钟中断,硬件故障 -- 外部事件
异常(内中断):系统调用,页故障,保护性异常,断点指令,程序性异常 -- 执行本身程序引发的
  • 工作原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
软硬件配合完成
硬件 -- 中断/异常的响应:
捕获中断源发出的 中断/异常请求,以一定方式响应,将处理器控制器交给特定的处理程序
软件 -- 中断/异常的处理程序:
识别 中断/异常类型 并完成相应处理

中断响应:
处理器中有 中断寄存器
响应过程: CPU执行每条指令后扫描中断寄存器,若有中断,将中断触发器内容编码进入PSW相应位(中断码),查中断向量表,执行中断处理程序

中断向量:
一个内存单元 - 存放 中断处理程序入口地址 和 程序运行所需的处理机状态字

中断处理程序:
设计操作系统时,为每一类中断编好处理程序,设置好中断向量表

linux中断向量表

  • x86处理器 的中断/异常
1
2
3
中断: 硬件引发
异常: 除零异常等, x86大概20种异常
系统调用: 用户态到内核态的唯一入口,是异常的一种
1
2
3
4
5
6
7
8
9
10
11
12
中断控制器(PIC 、 APIC) :将中断信号转换为 中断向量,引发CPU中断

实模式: 中断向量表
存放中断服务程序的入口地址

保护模式: 中断描述符表
采用 门(gate)描述符 数据结构 表示中断向量
门描述符类型:
任务门
中断门 (主要使用): 中断门后 系统自动禁止中断
陷阱门 (主要使用): 系统不禁止,可继续接收中断
调用门

中断/异常 的硬件处理过程

1
2
3
4
5
6
7
8
9
确定 中断向量i
通过IDTR寄存器,找到IDT中断描述符表, 查到第i项 中断描述符
通过GDTR寄存器,找到GDT段描述符表,根据中断描述符中的段描述符,从GDT中获取到 中断处理程序的 段基址
过程中需要多次特权级别检查

检查是否发送特权级别变化,是则堆栈切换 (用户态,内核态切换 - 使用相应权级的堆栈)
硬件压栈,保存上下文环境
如果是中断门, 则清IF位,禁止继续接收中断, 陷阱门则不管
根据 中断描述符的 段内偏移量 和 段描述符的段基址, 获取中断程序入口地址,进入执行

系统调用

1
2
3
4
5
6
7
8
9
10
用户在编程时可以调用的操作系统功能
CPU 从用户态 到 内核态的唯一入口

每种操作系统都提供几百种系统调用
进程控制
进程通信
文件使用
目录操作
设备管理
信息维护等等
  • 系统调用, 库函数, 内核函数 概念区分
1
2
3
4
内核函数 是 系统调用 的处理程序
库函数 是对 内核函数的分装 (如c库函数)

应用程序可以直接调用内核函数来进行系统调用, 但一般是操作 库函数 来进行
  • 系统调用机制的设计
1
2
3
4
5
6
7
8
9
10
中断/异常机制 -- 来支持 系统调用的实现
一条特殊指令, 陷入指令(又称访管指令) -- 引发异常,完成用户态到内核态的切换
系统调用号和参数 -- 每个系统调用事先给定编号(功能号)
系统调用表 -- 存放系统调用程序的入口地址


参数传递问题: 用户程序参数传递给内核
1. 陷入指令自带参数: 长度有限,参数有限
2. 通过寄存器传递(主要): 操作系统和用户程序都能访问的寄存器, 数量有限,参数有限
3. 内存中开辟专门的堆栈区
  • 系统调用执行过程
1
2
3
4
5
CPU 执行到特殊的陷入指令: 
中断/异常机制: 硬件保护现场; 查中断向量表, 最终把CPU控制权交给 系统调用总入口程序
系统调用总入口程序: 保存现场;参数保存进内核的堆栈,查系统调用表 把控制权转交给 相应系统调用的处理程序/内核函数
执行系统调用
恢复现场,返回用户程序
  • 基于x86内核的 linux系统调用实现
1
2
3
4
5
6
7
8
9
10
11
12
陷入指令 为128号:  
int $0x80
门描述符:
系统初始化时,对IDT表中的128号门初始化对应中断了
门描述符2,3字节标识 段选择符, 0,1,6,7字节为偏移量, 最终能对应到 system_call() 中断总入口
门类型: 15 陷阱门,支持调用过程仍允许接收中断
DPL特权级别: 3, 与用户级别相同, 允许用户进程使用该门描述符 (是用户发起的系统调用,所以必须是3)

执行 int $0x80 命令
特权级别的改变, 切换栈 用户栈 -》 内核栈。 CPU指向新的栈地址
信息压栈, 最后返回使用
找到 系统调用内核函数入口地址,执行

中断发送后操作系统的工作步骤