当前位置: 首页 > article >正文

进程状态并详解S和D状态

#define TASK_RUNNING 0x0000 // 运行或就绪(在运行队列)

#define TASK_INTERRUPTIBLE 0x0001 // 可中断睡眠(S状态)

#define TASK_UNINTERRUPTIBLE 0x0002 // 不可中断睡眠(D状态)

#define __TASK_STOPPED 0x0004 // 暂停(收到 SIGSTOP)

#define __TASK_TRACED 0x0008 // 被调试器追踪

#define EXIT_DEAD 0x0010 // 终止(最终状态)

#define EXIT_ZOMBIE 0x0020 // 僵尸进程

1.运行状态不一定非得是cpu在跑这个进程,只要是在跑或者在运行队列就行

2.浅睡眠状态S,比如sleep导致的,pcb挂到sleep创建的定时器的等待队列

3.深睡眠状态D,比如write时磁盘没准备好导致的,pcb挂到键盘的等待队列,深睡眠一般是进程进行IO时发生的

假如进程处于浅睡眠状态,如sleep,那进程会被挂在sleep创建的定时器的等待队列里,然后每次时钟中断,执行对应中断函数,会更新定时器,一旦等待时间够了,中断函数就会调用wake_up内核函数来唤醒等待进程,弄到运行队列里

假如进程处于D深度睡眠状态,如write时磁盘没准备好,进程会被挂在键盘的等待队列里,磁盘准备好后,键盘会发出硬件中断请求,然后cpu陷入内核,执行对应中断方法,中断函数会调用wake_up内核函数,使进程唤醒

进程在获取锁的时候,如果获取失败会切换到锁的等待队列上,等到其他进程执行完毕,unlock释放锁,unlock里面会调用wake_up内核函数,使锁上的进程切换到运行队列

wake_up内核函数(不是系统调用)

1.将进程的状态变成R

2.将进程切换到运行队列里

下面是详细过程:

1. 浅睡眠(S状态)—— 以 sleep() 为例​

​流程​
  1. ​进程调用 sleep(3)
    • 用户态 sleep() → 内核 nanosleep() 系统调用 → 设置高精度定时器(hrtimer)。
  2. ​加入等待队列​
    • 进程状态设为 TASK_INTERRUPTIBLE(S状态),挂入定时器的等待队列。
  3. ​时钟中断处理​
    • 每次时钟中断(如 tick_sched_timer())检查定时器是否到期。
    • ​到期时​​:调用 wake_up() 将进程移回运行队列,状态改为 TASK_RUNNING

​2. 深睡眠(D状态)—— 以 write() 写入磁盘文件为例​

​流程​
  1. ​进程调用 write()
    • 陷入内核-> 若磁盘写入繁忙,进程设为 ​TASK_UNINTERRUPTIBLE(D状态)​​,挂入磁盘设备的等待队列
  2. ​硬件中断触发​
    • 磁盘准备就绪 → 发送中断请求(IRQ)→ CPU 陷入内核,执行磁盘中断处理程序
  3. ​中断处理唤醒​
    • 中断程序确认写入完成 → 调用 wake_up() 唤醒磁盘等待队列中的进程

3. 锁竞争 —— 以 mutex 为例​

​流程​
  1. ​进程A 抢锁失败​
    • 调用 mutex_lock() → 若锁被占用,进程设为 TASK_UNINTERRUPTIBLE,挂入锁的等待队列。
  2. ​进程B 释放锁​
    • 调用 mutex_unlock() → 检查等待队列 → 调用 wake_up() 唤醒进程A。
  3. ​进程A 重新调度​
    • 被唤醒后状态改为 TASK_RUNNING,参与调度。

理解进程状态S和D的差别,不会被信号打断究竟在说什么

scanf的底层是read,read的时候stdin没数据,read会将进程设为S状态,pcb挂在stdin的等待队列,然后cpu会重新调度其他进程,按下ctrl+c,假如此时cpu运行的是进程B,触发硬件中断,cpu陷入内核,保存寄存器到进程B的内核栈,执行中断函数,会给等待队列中pcb中加入SIGINT信号并检查状态是S那就wake_up唤醒,然后切换到进程B用户态,等到cpu重新调度进程A,从上次read的阻塞点继续执行,会先判断有没有要处理的信号,如果有SIGINT,被忽略那就继续阻塞,如果不忽略,那就read返回EINTR,系统调用read执行完在即将切换回用户态时 ,在这个安全点处理信号SIGINT

假如进程A write时磁盘没有就绪,那就调用schedule()重新调度其他进程,write把进程A设为D状态,挂到磁盘等待队列,ctrl+c键盘硬件中断,执行中断函数,将磁盘等待队列里进程设置SIGINT的信号,然后检查进程状态是D状态,于是不会调用wake_up。直到磁盘准备就绪,发送硬件中断请求,可能在发生中断前cpu正在跑进程B,那么就陷入内核,把寄存器保存到进程B的内核栈,执行中断函数调用wake_up内核函数唤醒进程,然后切换到进程B用户态,等到cpu重新调度进程A,从write的阻塞点继续执行,直接开始写无论信号有没有被设置,write系统调用结束后在切换回用户态前执行SIGINT的执行函数 

不会被信号打断有两点

1.设置信号后不会调用wake_up使进程唤醒

2.重新调度进程从阻塞点开始运行,不会检查信号的设置,而是执行剩余逻辑

http://www.lryc.cn/news/2379217.html

相关文章:

  • 数据获取_Python
  • <前端小白> 前端网页知识点总结
  • 历史数据分析——宁波海运
  • 小结:jvm 类加载过程
  • OpenCv高阶(八)——摄像头调用、摄像头OCR
  • Java开发经验——阿里巴巴编码规范实践解析3
  • MySQL——6、内置函数
  • MySQL如何查看某个表所占空间大小?(表空间大小查看方法)
  • 软件架构之-论软件系统架构评估以及应用
  • 低延迟与高性能的技术优势解析:SmartPlayer VS VLC Media Player
  • pytorch小记(十九):深入理解 PyTorch 的 `torch.randint()` 与 `.long()` 转换
  • 深入解析Spring Boot与微服务架构:从入门到实践
  • 【交互 / 差分约束】
  • 宝塔面板部署前后端项目SpringBoot+Vue2
  • 现代生活健康养生新视角
  • 鸿蒙Next API17新特性学习之如何使用新增鼠标轴事件
  • 多模态大语言模型arxiv论文略读(八十一)
  • 3.4/Q2,Charls最新文章解读
  • 通过觅思文档项目实现Obsidian文章浏览器在线访问
  • Python列表全面解析:从入门到精通
  • 5月18总结
  • 赋予AI更强的“思考”能力
  • Linux Bash | Capture Output / Recall
  • 2025/5/18
  • 基于Quicker构建从截图到公网图像链接获取的自动化流程
  • LeetCode算 法 实 战 - - - 双 指 针 与 移 除 元 素、快 慢 指 针 与 删 除 有 序 数 组 中 的 重 复 项
  • uniapp自定义日历计划写法(vue2)
  • Java IO框架
  • 数据库2——查询
  • Mamba LLM 架构简介:机器学习的新范式