中断异常和系统调用
# 中断、异常和系统调用
- 系统调用(system call)
- 应用程序主动向操作系统发出的服务请求
- 异常(exception)
- 非法指令或者其他原因导致当前指令执行失败
(如:内存出错)后的处理请求
- 非法指令或者其他原因导致当前指令执行失败
- 中断(hardware interrupt)
- 来自硬件设备的处理请求
# 中断、异常和系统的调用比较
- 源头
- 中断:外设
- 异常:应用程序意想不到的行为
- 系统调用:应用程序请求操作提供服务
- 响应方式
- 中断:异步
- 异常:同步
- 系统调用:异步或同步
- 处理机制
- 中断:持续,对用户应用程序是透明的
- 异常:杀死或者重新执行意想不到的应用程序指令
- 系统调用:等待和持续
# 中断
# 中断处理机制
先硬件处理再软件处理
- 硬件处理
- 在CPU初始化时设置中断使能标志
- 依据内部或外部事件设置中断标志
- 依据中断向量调用相应中断服务例程
- 在CPU初始化时设置中断使能标志
- 软件处理
- 现场保存(编译器)
- 保存中断前的状态
- 中断服务处理(服务例程)
- 清除中断标记(服务例程)
- 现场恢复(编译器)
- 恢复到中断前的状态
- 现场保存(编译器)
# 中断嵌套
- 硬件中断服务例程可被打断
- 不同硬件中断源可能硬件中断处理时出现
- 硬件中断服务例程中需要临时禁止中断请求
- 中断请求会保持到CPU做出响应
- 异常服务例程可被打断
- 异常服务例程执行时可能出现硬件中断
- 异常服务例程可嵌套
- 异常服务例程可能出现缺页
# 系统调用
- 应用程序调用
printf()
时,会触发系统调用write()
- 操作系统服务的编程接口
- 通常由高级语言编写(C或者C++)
- 程序访问通常是通过高层次的API接口而不是直接进行系统调用
- 三种最常用的应用程序编程接口(API)
- Win32 API 用于 Windows
- POSIX API 用于 POSIX-based systems (包括UNIX,LINUX,Mac OS X的所有版本)
- Java API 用于JAVA虚拟机(JVM)
# 系统调用的实现
- 每个系统调用对应一个系统调用号
- 系统调用接口根据系统调用号来维护系统调用表的索引
- 系统调用接口调用内核态中的系统调用功能实现,并返回系统调用的状态和结果
- 用户不需要知道系统调用的实现
- 需要设置调用参数和获取返回结果
- 操作系统接口的细节大部分都隐藏在应用编程接口后
- 通过运行程序支持的库来管理
# 函数调用和系统调用的不同处
- INT和IRET指令用于系统调用
- 系统调用时,会进行堆栈切换和特权级的转换
- 函数调用
CALL
和RET
用于常规调用- 常规调用时没有堆栈切换
# 中断异常和系统调用的开销
- 超过函数调用
- 开销:
- 引导机制
- 建立内核堆栈
- 验证参数
- 内核态映射到用户态的地址空间
- 更新页面映射权限
- 内核态独立地址空间
- TLB
# 系统调用示例
- 为了与示例匹配,可以查看 lab8_answer
- 可从 🔗 此处 (opens new window) 下载 la8_answer
# 文件复制过程中的系统调用序列
// System call numbers
#define SYS_fork 1
#define SYS_exit 2
#define SYS_wait 3
#define SYS_pipe 4
#define SYS_write 5 // 与文件复制相关
#define SYS_read 6 // 与文件复制相关
#define SYS_close 7 // 与文件复制相关
#define SYS_kill 8
#define SYS_exec 9
#define SYS_open 10 // 与文件复制相关
#define SYS_mknod 11
#define SYS_unlink 12
#define SYS_fstat 13
#define SYS_link 14
#define SYS_mkdir 15
#define SYS_chdir 16
#define SYS_dup 17
#define SYS_getpid 18
#define SYS_sbrk 19
#define SYS_sleep 20
#define SYS_procmem 21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ucore 中相关应用函数
在ucore中库函数
read()
的功能是读文件- user/libs/file.h:
int read(int fd, void * buf, int length)
- user/libs/file.h:
库函数
read()
的参数和返回值int fd
: 文件句柄void * buf
: 数据缓冲区指针int length
: 数据缓冲区长度int return_value
: 返回读出数据长度
库函数
read()
使用示例- in sfs_filetest1.c:
ret = read(fd, data, len)
- in sfs_filetest1.c:
# 由应用态转到内核态调用
sfs_filetest1.c: ret = read(fd,data,len);
……
// 此段代码主要用于压栈
8029a1: 8b 45 10 mov 0x10(%ebp),%eax
8029a4: 89 44 24 08 mov %eax,0x8(%esp)
8029a8: 8b 45 0c mov 0xc(%ebp),%eax
8029ab: 89 44 24 04 mov %eax,0x4(%esp)
8029af: 8b 45 08 mov 0x8(%ebp),%eax
8029b2: 89 04 24 mov %eax,(%esp)
8029b5: e8 33 d8 ff ff call 8001ed <read>
syscall(int num, ...) {
// 相当于调用 kern/trap/trapentry.S 中的 alltraps
...
asm volatile (
"int %1;"
: "=a" (ret)
: "i" (T_SYSCALL),
"a" (num),
"d" (a[0]),
"c" (a[1]),https://github.com/sbwcwso/sbwcwso.github.io/tree/main/md-file/0-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/1-%E6%B8%85%E5%8D%8E%E5%85%AC%E5%BC%80%E8%AF%BE/3-%E5%90%AF%E5%8A%A8%E4%B8%AD%E6%96%AD%E5%BC%82%E5%B8%B8%E5%92%8C%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8/assets/lab8_result
"b" (a[2]),
"D" (a[3]),
"S" (a[4])
: "cc", "memory");
return ret;
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# ucore系统内核态用软中断实现系统调用的过程
- kern/trap/trapentry.S:
alltraps()
- 软中断
- kern/trap/trap.c:
trap(), trap_dispatch()
tf->trapno == T_SYSCALL
T_SYSCALL
为系统调用对应的中断向量- 通过查询中断向量表,判断是系统调用,然后进入系统调用表进行查询
- kern/syscall/syscall.c:
syscall()
tf->tf_regs.reg_eax == SYS_read
- 由系统调用表确定需要调用的系统函数是
SYS_read
,然后开始执行相应的函数
- kern/syscall/syscall.c:
sys_read()
- 从
tf->sp
获取fd, buf, length
- 从堆栈 sp 中获取参数
- 调用相关函数实现文件读取
- 从
- kern/fs/sysfile.c:
sysfile_read()
- 读取文件,并返回上级调用函数,直至
alltraps()
- 读取文件,并返回上级调用函数,直至
- kern/trap/trapentry.S:
trapret()
trapret()
: 将读到的内容返回到用户态- 具体通过
ireturn
来实现
- 具体通过
Understand 的版本是 5.1
创建项目注意框选进一步配置
将 C/C++ 的分析模式设为
Fuzzy
File Types 中确保 .S 对应的是 Assembly,如果没有相应的项,需要新建文件
将 Assembly 设置为 IBM
编辑 (opens new window)