进程状态与转换
进程状态与转换
复习定位
操作系统中的进程在任何时刻处在一个唯一的状态中。三状态模型(就绪/运行/阻塞)是最简模型。就绪态的进程"万事俱备只差CPU"——被调度后进入运行态。阻塞态的进程在等某个事件(磁盘I/O完成、锁被释放、定时器到期)——操作系统在有事件到达之前不会分配给它CPU。
三状态模型的严格转换
就绪态→运行态:调度程序(在时钟中断或系统调用返回前)从就绪队列中选出一个进程、恢复其寄存器上下文——开始执行。
运行态→就绪态:时间片用完被抢占、或被更高优先级进程打断——进程被放回就绪队列。
运行态→阻塞态:进程主动调用需要等待的系统调用(如read()从磁盘读数据——数据不在缓冲区中)。进程进入等待队列——调度器选择另一个就绪进程运行。
阻塞态→就绪态:等待的事件完成了(硬盘中断到来→数据读入缓冲区→被阻塞的进程移出等待队列加入就绪队列)。这一转换由内核的中断处理程序或内核线程触发——被阻塞的进程本身不参与此决策。
注意阻塞态和就绪态的区别——就绪态只缺CPU这个单一资源——阻塞态缺某个数据/事件——给了CPU也无法前进。所以操作系统绝不对阻塞进程分配CPU。
五状态模型的扩充
在创建和终止之间增加了两个临时状态:
新建(New)——进程正在被创建但尚未加入就绪队列——正在分配task_struct、设置初期的进程权限和地址空间映射。在此状态下——进程还没有开始执行——如果系统资源不足(内存不够、进程表满)——新建可能直接拒绝进入就绪态。
终止(Terminated)——进程已经结束了执行但task_struct未被回收(父进程还没有wait)。此时进程是僵尸——不占用内存(地址空间已经释放)——但占用PID和PCB条目。
加上运行、就绪和阻塞之后的五状态模型:
New → Ready ↔ Running ←→ Blocked → Terminated
↓ ↑
+────────────────────+ (阻塞事件完成)睡眠态的分割: TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE
Linux内核将阻塞态进一步拆分为两个状态:
S(TASK_INTERRUPTIBLE)——可中断睡眠:进程在等待条件的到来——如等待socket数据、semaphore、wait_queue条目。在此状态下进程可以接收和处理信号——收到信号后进程被唤醒处理信号——如果信号是SIGKILL则直接终止。这是最常见的阻塞态。
D(TASK_UNINTERRUPTIBLE)——不可中断睡眠:进程在内核态进行I/O操作(正在等待磁盘驱动的DMA传输完成)。这一状态通常不被信号打断——因为中断I/O可能破坏操作的数据完整性、产生没有来得及解锁的内核资源锁。不可中断睡眠是特殊的——进程不接受任何信号(kill -9也不能强制结束此进程)。如果进程长时间处在D状态——通常是硬件问题(磁盘控制器故障、NFS挂载的服务端崩溃)或内核驱动bug。
进程的挂起与恢复
当内存不足时——操作系统可以将一些非活跃进程的整个地址空间换出(swap out)到磁盘交换区——进程进入挂起态。挂起态可以叠加在就绪态和阻塞态之上——就绪挂起的进程在交换区中——再调度时需要先换入内存——有显著的I/O延迟。阻塞挂起的进程也在交换区——等待的事件完成后——OS将它从阻塞挂起转移到就绪挂起——但需要等到换入内存才能被调度。
复习检查
就绪态和阻塞态的区别是什么?在Linux中如何判断一个进程是在等CPU还是等I/O?
不可中断睡眠(D状态)的进程为什么不能被kill -9杀掉?如果磁盘故障导致进程无限期留在D状态——系统管理员如何解决?
进程是如何从运行态进入阻塞态的?是内核主动中断还是进程自己发起一个需要等待的系统调用实现的?
僵尸进程(Z状态)在终结前占用的资源——向父进程发送SIGCHLD后——父进程如果忽略SIGCHLD——子进程的task_struct会被谁释放?
如果一个进程有大量匿名页面被交换到交换区(Swap Out)——当该进程被调度运行时——缺页异常会逐步换入被交换出去的页——首次恢复运行时哪些内核状态必须同步加载才能让进程正常运行?