1 系统调用: 应用程序 (含 运行库 ) 与 OS Kernel 间 接口
(1) Linux 系统调用
eax
`内核态: 表示 `系统调用号`
1 exit: 退出进程
2 fork: 创建进程
3 read: 读 文件或IO
4 write: 写 文件或IO
...
系统调用 返回 `用户态`: 作 `系统调用 returnValue`
(2) 系统调用: 各 OS 不兼容
解决: 加 `中间层: 运行库 -> 标准库`
2 系统调用 原理
用户态: 应用程序
|
| 进入: 用 中断 (Interrupt)
|/
内核态
2.1 中断
(1) 是什么?
是 `硬件或软件` 的 `请求`, 要求 `CPU 暂停` 当前工作 `转手 去处理` 更重要的工作
(2) 如何被 CPU 获取 ?
信号 机制, 信号就是一种 中断
1] 键盘 `有键按下` 时, 键盘芯片 `发信号 给 CPU`
2] `CPU 收到 信号`, 知道 有键按下, `去询问` 键盘被按下的是 `哪个键`
(3) 属性
中断号 (从 0 开始)
`中断处理程序 (ISR, Interrupt Service Routine): Linux 下 int 0x80 相应的 ISR 恒为 system_call`
(4) 类型
硬件中断
硬件异常 或 其他事件, 如 电源掉电、键盘被按下的是
软件中断
Linux int0x80 指令
(5) Linux 中断: 中断号 0x80
[1] eax 存 `系统调用号`
[2] 中断指令 int 0x80 触发 系统调用
1] 查 中断向量表: pointer array
Linux
第 n 项: functionPtr 指向 `第 n 号中断的 ISR = system_call`
2] 查 `系统调用表` + 系统调用号 -> `系统调用 (sys_fork) 的 funcAddr`
2.2 Linux 中断流程
(1) 触发 中断 : int 0x80
main -> fork
// fork 的伪代码
pid_t fork(void)
{
long __res;
eax = __NR_fork; // (1) `系统调用号`(宏 __NR_fork) 放 eax
int 0x80 // (2) 中断号 0x80 的 指令 int 0x80 触发 系统调用 -> 进入 内核态
__res = eax // (3) 返回用户态: eax 可用于存 `系统调用 的 返回值`
__syscall_return(pid_t, __res) // (4) 宏: check 系统调用 的 returnValue, `转换为 errno 错误码`
}
(2) 用户栈/态 <- - - 切换 - - -> 内核栈/态
1) int 指令 (触发)
[1] 先 切换栈
用户栈(态) -> (被 CPU 自动) 切换到 -> 内核栈(态)
如何切?
1] `找到 内核栈` (每个进程 都有自己的 内核栈)
1] `用户态 register(SS ESP EFLAGS CS EIP 依次压栈)` 保存到 内核栈
3] ESP 指向 内核栈栈顶
2) 再 执行 ISR
2) iret 指令
内核栈(态) -> 用户栈(态)
弹栈: 弹出 用户态 register
3) 当前栈
SS : 指向 当前栈所在 Page
ESP: 指向 栈顶 (用户栈/内核栈)
4) PC = CS:EIP = CS << 4 | EIP
(3) sys 系统调用
如何获取 用户参数
?
1) 用户 para1~6 -> 被保存到(用 ... 传递) 6 个 register ebx/ecx/edx/esi/edi/ebp
2) 6 个 register 被 system_call 调的 宏 SAVE_ALL 保存到 `内核栈`
3) 宏 asmlinkage 让 `sys 系统调用` 只从 内核栈上取 `用户 para/6个register`
CPU 中断过程.jpg
中断流程: Linux OS.jpg
中断时 用户栈 和 内核栈 切换.jpg
系统调用 流程: Linux OS.jpg
系统调用 时 内核栈分布.jpg
系统调用 中 如何向 OS Kernel 传 用户态 参数: Linux OS.jpg
网友评论