去做点别的事情吧,等天黑了再回来
在讨论中断前,我们先明确一点,任务发生中断,就意味着需要操作系统进行介入,中断是用户态切换到内核态的唯一途径,中断的处理是在内核态进行的
硬中断、软中断和任务
- 硬中断是由硬件中断产生的,具有最高优先级的任务
- 软中断是由软件中断产生的,优先级第二高的任务
- 一般的用户态线程优先级第三
- 当一个核上无事可做时,位于IDLE状态
中断和异常的种类
所有的同步异常都使用0-31的中断向量,并映射至中断描述表的0-31。高于31的部分为软中断,即通过int
指令触发的中断或者由外部设备触发的异步硬件中断。
中断处理
在linux中,中断被分为上半部分和下半部分
上下半部的理解
上半部(top half)——紧急硬件操作
下半部(buttom half)——非紧急的耗时操作
中断向量
中断向量的地址是中断服务例程的入口地址
中断和子程序调用的比较
相同点
都需要压栈保护现场
不同点
中断处理程序会保存程序状态字(PSW)寄存器,记录处理器状态和控制指令执行顺序,从而实现现场保护与恢复;而子程序调用在进程内执行,不需要更改PSW
系统调用
系统调用是内核对外实现服务的接口,类似于用户和银行柜员,用户可以发起取钱的服务,由银行柜员处理,但用户不能直接到银行中拿钱。
系统调用的作用
系统调用主要涉及以下几个方面的作用:
- 设备管理:设备启动/请求/释放
- 文件管理:文件创建/读/写/删除
- 进程控制:进程创建/销毁/阻塞/唤醒
- 进程通信:消息传递/信号传递
- 内存管理:内存分配/回收
系统调用实现机制
首先,内核对系统调用的处理是给所有系统调用一个中断向量号,将其视为一个软中断,然后再根据系统调用编号,在系统调用表中进行寻找。
系统调用与函数调用的区别
指令集不同
系统调用时使用INT
和IRET
指令(中断指令),产生堆栈切换和特权级切换(用户态和内核态堆栈不同,用户不能访问内核态堆栈);而常规函数调用采用CALL
和RET
,没有发生堆栈切换。
开销不同
系统调用由于发生了堆栈切换,因此开销更大
- 中断引导
- 建立内核堆栈
- 验证参数有效性
- 内核态映射到用户态的地址空间,使得
- 更新页面映射权限
- 内核态独立地址空间
- TLB(页表缓存)内容更新
系统调用实例
现场保护
trapframe1
trapframe 保存了中断发生时需要保存的相关寄存器,这个数据结构很重要,现在对其进行一些总结。一个完整的Trapframe,其布局如下:
可以看到trapframe被分为了三个部分,有一部分由int
指令保存,即硬件实现,其余部分由软件实现。在发生用户态到内核态的切换后,trapframe将被保存在内核栈中。其中,tf
指向oldesp
trapframe和栈之间的关系:
trap
函数接收一个trapframe
作为变量,而我们的trapframe是在栈上进行构造的。为了进一步地探究trapframe与栈之间的关系,我们将两者的内容分别进行打印。
1 | TRAP frame at 0xefffffbc from CPU 0 |
可以看到trapframe
的地址为0xefffffbc
,在进入trap
函数后,栈的内容如下:
1 | (gdb) x/30x $esp |
可以看到,从0xefffffbc
开始,即上面的**圈住的内容,就是trapframe的内容。从这里我们也能看出,临时变量实际上就是栈的某个范围内的内容。