中断、异常和系统调用

去做点别的事情吧,等天黑了再回来

在讨论中断前,我们先明确一点,任务发生中断,就意味着需要操作系统进行介入,中断是用户态切换到内核态的唯一途径,中断的处理是在内核态进行的

硬中断、软中断和任务

  • 硬中断是由硬件中断产生的,具有最高优先级的任务
  • 软中断是由软件中断产生的,优先级第二高的任务
  • 一般的用户态线程优先级第三
  • 当一个核上无事可做时,位于IDLE状态

中断和异常的种类

所有的同步异常都使用0-31的中断向量,并映射至中断描述表的0-31。高于31的部分为软中断,即通过int指令触发的中断或者由外部设备触发的异步硬件中断。

中断处理

在linux中,中断被分为上半部分和下半部分

上下半部的理解

上半部(top half)——紧急硬件操作

下半部(buttom half)——非紧急的耗时操作

中断向量

中断向量的地址是中断服务例程的入口地址

中断和子程序调用的比较

相同点

都需要压栈保护现场

不同点

中断处理程序会保存程序状态字(PSW)寄存器,记录处理器状态和控制指令执行顺序,从而实现现场保护与恢复;而子程序调用在进程内执行,不需要更改PSW

系统调用

系统调用是内核对外实现服务的接口,类似于用户和银行柜员,用户可以发起取钱的服务,由银行柜员处理,但用户不能直接到银行中拿钱。

系统调用的作用

系统调用主要涉及以下几个方面的作用:

  • 设备管理:设备启动/请求/释放
  • 文件管理:文件创建/读/写/删除
  • 进程控制:进程创建/销毁/阻塞/唤醒
  • 进程通信:消息传递/信号传递
  • 内存管理:内存分配/回收

系统调用实现机制

首先,内核对系统调用的处理是给所有系统调用一个中断向量号,将其视为一个软中断,然后再根据系统调用编号,在系统调用表中进行寻找。

系统调用与函数调用的区别

指令集不同

系统调用时使用INTIRET指令(中断指令),产生堆栈切换和特权级切换(用户态和内核态堆栈不同,用户不能访问内核态堆栈);而常规函数调用采用CALLRET,没有发生堆栈切换。

开销不同

系统调用由于发生了堆栈切换,因此开销更大

  • 中断引导
  • 建立内核堆栈
  • 验证参数有效性
  • 内核态映射到用户态的地址空间,使得
    • 更新页面映射权限
  • 内核态独立地址空间
    • TLB(页表缓存)内容更新

系统调用实例

现场保护

trapframe1

trapframe 保存了中断发生时需要保存的相关寄存器,这个数据结构很重要,现在对其进行一些总结。一个完整的Trapframe,其布局如下:

图片名称

可以看到trapframe被分为了三个部分,有一部分由int指令保存,即硬件实现,其余部分由软件实现。在发生用户态到内核态的切换后,trapframe将被保存在内核栈中。其中,tf指向oldesp

trapframe和栈之间的关系:

trap函数接收一个trapframe作为变量,而我们的trapframe是在栈上进行构造的。为了进一步地探究trapframe与栈之间的关系,我们将两者的内容分别进行打印。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
TRAP frame at 0xefffffbc from CPU 0
edi 0x00000000
esi 0x00000000
ebp 0xeebfdfe0
oesp 0xefffffdc
ebx 0x00000000
edx 0x00000000
ecx 0x00000000
eax 0x00000002
es 0x----0023
ds 0x----0023
trap 0x00000030 System call
err 0x00000000
eip 0x00800d17
cs 0x----001b
flag 0x00000046
esp 0xeebfdfd4
ss 0x----0023

可以看到trapframe的地址为0xefffffbc,在进入trap函数后,栈的内容如下:

1
2
3
4
5
6
7
8
9
(gdb) x/30x $esp
0xefffff98: 0xefffffbc 0x00000000 0x00000000 0x00000000
0xefffffa8: 0x00000000 0x00000000 0xeebfdfe0 0xf0104806
0xefffffb8: 0xefffffbc ** 0x00000000 0x00000000 0xeebfdfe0
0xefffffc8: 0xefffffdc 0x00000000 0x00000000 0x00000000
0xefffffd8: 0x00000002 0x00000023 0x00000023 0x00000030
0xefffffe8: 0x00000000 0x00800d17 0x0000001b 0x00000046
0xeffffff8: 0xeebfdfd4 0x00000023 ** 0xf000ff53 0xf000ff53
0xf0000008: 0xf000e2c3 0xf000ff53

可以看到,从0xefffffbc开始,即上面的**圈住的内容,就是trapframe的内容。从这里我们也能看出,临时变量实际上就是栈的某个范围内的内容。

参考文献

0%