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

进程状态并详解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.xdnf.cn/news/520129.html

相关文章:

  • C++学习:六个月从基础到就业——C++17:结构化绑定
  • 什么是dom?作用是什么
  • 产品周围的几面墙
  • C++高级用法--绑定器和函数对象
  • 垂直智能体:企业AI落地的正确打开方式
  • [人月神话_6] 另外一面 | 一页流程图 | 没有银弹
  • 三:操作系统线程管理之用户级线程与内核级线程
  • 大模型应用开发工程师
  • 从逻辑学视角探析证据学的理论框架与应用体系;《证据学》大纲参考
  • Java学习手册:服务熔断与降级
  • 朴素贝叶斯
  • 做什么, what to do?
  • 面试题总结二
  • atcoder C - ~
  • EmuEdit
  • 网页 H5 微应用接入钉钉自动登录
  • python29
  • 【从基础到模型网络】深度学习-语义分割-ROI
  • C++ - 网络编程之初始连接(Winsock2 概述、初始连接案例、初始连接案例解读)
  • 封装、继承、多态的理解
  • Java面试实战:从Spring Boot到分布式缓存的深度探索
  • 程序代码篇---python获取http界面上按钮或者数据输入
  • 批量下载AlphaFold结构
  • leetcode刷题日记——翻转二叉树
  • 第11章 JDBC与MySQL数据库
  • UI架构的历史与基础入门
  • GOP模式调节画面质量讲解
  • 八股碎碎念01——HashMap原理
  • GESP编程能力等级认证C++3级1-数组1
  • 研读论文《Attention Is All You Need》(6)