3. CPU 和指令系统|中断机制

重要的四个概念区分

Interrupts Exceptions Trap System call
Interrupts are asynchronous events triggered by external devices. Exceptions are synchronous events caused by instruction execution. A trap is a synchronous transfer of control to the OS, typically used for system calls or debugging.

Traps are a type of exception that return to the next instruction.
System calls are implemented via traps.

A system call is a controlled entry point into the kernel.
中断的特点:

* 来自于硬件
* 和 CPU 当前正在执行的指令无关,是异步的.

* 例如键盘输入、磁盘完成、时钟中断、网络包到达。

目的是通知 CPU 有硬件事件发生,需要 OS 介入处理。
异常的特点
* 是 CPU 执行某个指令的过程中发生的,和当前正在执行的指令是同步的。
* 例如除 0、page fault(缺页、也叫缺页中断)、访问越界。
* Trap 本质是一种特殊的异常(a sub class of exceptions),由软件主动触发,和当前执行的指令是同步的。

* 用途:系统调用(syscall)、断点 (breakpoint))、调试陷阱 (debug trap)
* 和 Exceptions 的区别是 Trap 是故意触发的,而 Exceptions 是错误,是预期之外的。

* 系统调用就是用户程序通过 trap 指令主动让 CPU 转入内核态,来执行内核服务。
【注:408 中管 Exceptions 叫做内部中断。即 408 中没有这些区分,Interrupts 和 Exceptions 通通叫中断,然后把中断分了个内部中断和外部中断,纯纯傻逼行为。】
【Trap 和 System call 也是,统称为系统调用。】

中断机制

中断的本质是硬件或者 CPU 主动打断当前执行的流程,让内核处理事件的一种通知机制。整个过程由硬件+软件一起完成。

中断可以分为两类,根据是不是发生在 CPU 核心内部(ALU、寄存器、流水线等),分为内部中断和外部中断。
内部中断例如 ALU 中除 0、MMU 中缺页,
外部中断例如计时器到时、硬盘、键盘、网卡等(计时器虽然封装在 CPU 里,但是属于 CPU 的外围设备,不在 Core 里)

进程 P 通过执行系统调用从键盘接受一个字符输入的过程:

  1. 【用户程序调用】用户进程执行 read(STDIN_FILENO, buf ,1) ,触发 trap,在 x86 中叫做 syscall.
    1. 触发 trap 后,硬件自动完成以下
      1. 保存当前用户态寄存器
      2. 切换到内核态
      3. 切换到内核栈
      4. 跳转到系统调用入口 syscall_entry
  2. 【内核程序处理】内核执行对应的系统调用函数(假设为 sys_read, 通过 syscall_entry 进入),内核检查键盘缓冲区为空,故 read 无法立刻返回。于是内核将程序 P 放入到阻塞队列中. 并通过 schedule() 切换到另外一个进程执行。
  3. 【用户通过硬件发中断】用户在键盘上输入字符,键盘控制器向 CPU 发送硬件中断,CPU 响应
    1. 自动切换到内核态(如果此时 CPU 是用户态的话)
    2. 自动保存当前执行现场(现场:context 是包含断点的,断点指的是程序计数器 PC、程序状态字等信息。)
      1. 断点:由硬件(也叫中断隐指令)保存完成,包含 PC 计数器、程序状态字等信息。
      2. 其他寄存器由软件完成,在对应的 syscall_entry 的汇编程序的入口。
    3. 根据 IDT 跳转到对应的中断服务例程(ISR)
  4. 【内核的中断处理程序执行】启动键盘中断处理程序(ISR)
    1. ISR 只扫描哪个按钮按下, 然后传递给键盘驱动程序。
    2. 键盘驱动程序继续处理。
      1. 将字符从键盘控制器读入系统缓冲区(键盘驱动程序做的事情:将按钮映射为键值、处理修饰键、更新键盘状态、写入 input event ring buffer、通过 wake_up() 唤醒被阻塞的进程)
      2. 将进程 p 插入到就绪队列(是键盘驱动程序做的)
  5. 【又调度到了用户程序】进程 P 从系统调用返回(C 语言层面)
    1. 系统调用出口(内核汇编层)
    2. CPU 执行iretq 汇编指令,CPU 返回到用户态。
    3. 执行之前的指令(Exceptions 类,例如缺页中断)或执行下一条指令(外部中断、Trap、System Call,例如键盘输入)。

中断的过程为:

  1. 保护现场(现场 context 是包含断点的,断点指的是程序计数器 PC、程序状态字等信息。)
  2. 切换到内核态
  3. 查找 IDT(中断描述符表)
  4. 跳转到对应的中断处理程序(这些中断处理程序并不一定都是内核本来就带的,可能是设备制造商的驱动程序,不过这种驱动程序本身就是内核代码的一部分)
  5. 处理完成后通过 iret 返回先前执行点,执行中断前的指令(缺页中断,要重新执行)或者下一条指令(硬件中断,例如键盘输入)。

对比系统调用:

  1. 保存当前执行状态(用户态寄存器)
  2. 切换到内核态
  3. 切换到内核栈
  4. 跳转到系统调用表处理对应的函数(这些系统调用函数都是内核的)
  5. 执行完成后返回用户态,执行下一条指令。