003-内核的心脏起搏与大脑切换:Linux 中断、时钟与进程调度深度解密 内核的心脏起搏与大脑切换Linux 中断、时钟与进程调度深度解密当一个普通用户敲击键盘或者一个程序在后台默默计算时Linux 内核在做什么这不再是苍白的理论而是几个真正意义上“指挥电脑硬件工作”的环节。一、硬件到 CPU 的“紧急呼叫”中断机制8259A 芯片CPU 不能时刻盯着所有的硬件键盘、鼠标、网卡、硬盘。为了让它们能主动联系 CPU硬件设计了**中断Interrupt**系统。1. 谁在负责中转—— 8259A 可编程中断控制器在早期的 PC 架构中有一块专门负责“接电话”的芯片——8259A。它的作用是收集所有设备发来的信号排队后统一传给 CPU。如图 2-5你上传的第一张图所示为了扩展能力工程师用了两块 8259A 芯片级联主片Master直接连 CPU地址在0x20管理IRQ0 ~ IRQ7。从片Slave连在主片的 IRQ2 上地址在0xA0管理IRQ8 ~ IRQ15。这样PC 总共可以处理15 个独立的中断向量号。2. 重要设备的中断分配书中清晰地列出了每个 IRQ 对应的设备这几乎是早期 PC 的“标准配置”IRQ0时钟最核心、最准时的那个IRQ1键盘你敲下的每一个键IRQ3 / 4串行口COM1/2IRQ6软盘时代的眼泪IRQ12PS2 鼠标IRQ14硬盘当这些设备有动作时8259A 会通过INTR引脚向 CPU 发出“紧急呼叫”CPU 暂停手头工作转而处理这个中断。3. 两种中断硬件与软件硬件中断IRQ0~IRQ15来自外部设备。软件中断int 0x80书中特别提到Linux 用int 0x80作为系统调用System Call的入口。也就是说当你的程序想请求内核帮它干活比如读写文件它会通过这一行代码主动“违规闯入”内核申请服务。二、系统的“心脏起搏器”定时机制8253 芯片有了中断系统还得有一个基准时间用来控制多任务的公平运行。这就引出了 8253 可编程定时器。1. 100 次/秒的“滴答”声在 Linux 0.11 中8253 被设置成每10 毫秒10ms触发一次时钟中断IRQ0。这看起来很短但意味着 1 秒钟之内系统会收到100 次脉冲。每一次脉冲都会让全局计数器jiffies系统滴答数增加 1。这个jiffies就像是系统的生命计时器。2. 时钟中断的“灵魂”do_timer()每次 10ms 的脉冲都会触发timer_interrupt函数。这个函数会立即调用do_timer()。书中第二张图里写道“根据特权级对当前进程运行时间作累计。”用户态CPL0此时是普通程序在跑它的时间片会减少。内核态CPL1此时是内核在跑通常不减少时间片内核不应因时间中断而抢占。3. 时间片用完了怎么办书中最关键的一句话是“如果此时进程时间片已经递减为0表示该进程已经用完本次使用CPU的时间片于是程序就会根据被中断程序的级别…确定进一步的处理进而调用schedule()切换到其他进程去运行。”看懂了吗时钟中断不仅是在“计时”它是进程调度的核心触发器。三、进程的“身份证”与“换岗”进程控制task_struct当schedule()决定要换一个进程运行时光有决定是不够的它必须知道当前进程的状态并把新进程“唤醒”。1. 任务数据结构task_structLinux 为每个进程都发了一张“身份证”——进程控制块PCB在代码中被称为task_struct。这个结构体包含PID进程 ID状态运行、等待、暂停等CPU 寄存器状态这是所有现场信息优先级、信号、时间片剩余量堆栈指针2. 进程上下文与切换书中第三张图解释道“当内核需要切换switch至另一个进程时它需要保存当前进程的所有状态即保存当前进程的上下文。”这就像是运动员交接棒保存上下文当前进程的 CPU 寄存器EAX, EBX, ESP, EIP…全部存入它自己的task_struct中。恢复上下文从新进程的task_struct中把寄存器值重新加载进 CPU。继续执行CPU 从新进程暂停的地方继续往下执行。3. 内核栈与用户栈的分离书中特别提到进程在内核态系统调用时和用户态普通运行使用的是不同的堆栈。内核栈专门用于处理系统调用、中断等内核代码而用户栈存你的局部变量、函数参数。这种隔离设计极大地提高了安全性。四、全景图把散落的拼图拼在一起最后我们把这三节的内容组合起来你能清晰地看到 Linux 内核运转的核心闭环进程控制块 (PCB)定时器芯片 (8253)CPU (内核态)中断控制器 (8259A)硬件 (键盘/硬盘)进程控制块 (PCB)定时器芯片 (8253)CPU (内核态)中断控制器 (8259A)硬件 (键盘/硬盘)每10ms触发一次更新 jiffies 滴答数递减当前进程时间片alt[进程时间片用完][时间片未用完]发送IRQ0中断请求通过INTR引脚通知CPU保存当前进程上下文 (入内核栈)跳转至 timer_interrupt 处理函数调用 do_timer()将当前进程状态设为“就绪/等待”调用 schedule() 调度程序查找下一个待运行进程的PCB恢复新进程的上下文 (从PCB取回寄存器)恢复当前进程上下文返回继续执行用户代码这就是 Linux 0.11“心跳-中断-调度”的铁三角。硬件发出声音中断时钟负责节拍定时而task_struct负责保存每个程序演出的剧本上下文。明白了这三个环节你就掌握了操作系统调度的最核心原理。接下来就可以真正开始一行一行读sched.c的代码了