12章 系统调用(System Call) 与 API

2022-07-17  本文已影响0人  my_passion

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
上一篇下一篇

猜你喜欢

热点阅读