中断
中断具有两个属性,两者一一对应
- 中断号
- 中断处理程序
内核中存在一数组 中断向量表 ,保存中断号对应的中断处理程序位置
分类
- 软件中断
- 通常为一条指令带一个参数记录中断号,使用户可手动触发某个中断
- 由于中断号有限,因此仅使用少数几个中断号用以系统调用
- Windows int 0x2e
- Linux int 0x80
- 将系统调用号转为保存在固定寄存器中
- 硬件中断
系统调用
让应用程序有能力访问系统资源,让程序借助OS进行一些必须由操作系统支持的行为。
概念 系统调用是应用程序与操作系统之间的接口, 程序最终都需要使用系统调用来与内核交互. Windows对系统调用再次进行封装为DLL机制, 额外增加一层api 原理 防止系统有限的资源被多个不同应用程序同时访问, 将可能产生冲突的系统资源进行保护, 阻止直接访问.
往往通过中断实现.
基本要求
- 必须有明确的定义,每个调用的含义、参数、行为都有严格而清晰的定义
-
保证稳定和向后兼容。一般仅增加新的调用接口,而不改变原有调用。
- 缺陷
- OS 提供系统调用接口过于原始
- 操作系统之间系统调用互不兼容
解决方法: 增加层 - 运行库/标准库 优点: 源代码级别的可移植性 缺陷: 为满足可移植性, 只能取所有平台功能的交集.
系统调用过程
先查找 中断向量表,再查询 系统调用表 。
Linux系统调用
x86下,系统调用由0x80中断完成
- eax寄存器用以表示系统调用号
- eax=1
exit退出进程 - eax=2
fork - eax=3
read - eax=4
write - eax=5
open - eax=6
close
- eax=1
- 其他寄存器用以传递参数
内核中存在一数组 中断向量表 ,保存中断号对应的中断处理程序位置
缺点
- 使用不便,过于原始,缺少包装
- 各OS系统调用不兼容,定义及实现基本不同。
Linux系统调用实现

x86_32下系统调用参数至多6个,分别用 ebx ecx edx esi ebp 保存;
x86_64下参数用rdi rsi rdx rcx r8 e9和栈保存;
用户态和内核态使用不同栈,每个程序都同时有用户栈和内核栈。
流程:
- 执行
int 0x80,触发中断- 保护现场
- 切换到内核态
- 从用户栈切换到内核栈
- 保存当前ESP和SS寄存器值 到内核栈上(硬件完成)
- 找到当前进程的内核栈
- 在内核栈中依次压入用户态寄存器SS/ESP/EFLAGS/CS/EIP
- 将ESP和SS寄存器设为当前内核栈的相应值
- 保存当前ESP和SS寄存器值 到内核栈上(硬件完成)
- 查找中断向量表中0x80号元素
- 运行0x80号中断处理程序
- 中断处理程序
- 查找系统调用表
- 使用宏SAVE_ALL 保存各个寄存器到栈,系统调用从栈中取参数
- 执行对应系统调用
- 返回
- 返回
- 切换回用户栈
- 从内核栈中弹出SS/ESP/EFLAGS/CS/EIP的值,将栈恢复到用户态
- 退回到用户态
- 还原现场
- 继续下一条指令
- 切换回用户栈
Windows API
Win系统中最底层是Windows API, 以DLL导出函数形式提供给开发者,包含在系统DLL中。
Windows API => CRT 、 MFC => App
优点
- 隔离硬件结构的不同而导致的程序兼容性问题
系统调用非常依赖硬件结构,受到硬件严格限制。因此硬件的改变会对运行在不同硬件平台同一个系统下的应用程序造成巨大影响。
运行库
运行库作为系统调用和程序之间的一个抽象层。
- 优点:
- 使用便捷
- 在各系统上形式统一,具有源代码级别可移植性,跨平台
- 缺点:
- 直接使用系统调用性能更高
- 库函数只能各平台交集
例
- fopen()
- fread()
- fwrite()