• 【Linux】进程状态详解


    img

    Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法…感兴趣就关注我吧!你定不会失望。


    在这里插入图片描述

    0. 五状态进程模型

    这是操作系统中的五种状态.其中新建状态和退出状态不是很重要,所以通用的为三状态模型也就是:就绪态,阻塞态,运行态

    • 就绪态:需要CPU的进程已经完成了资源调度,在等待分配到CPU
    • 运行态:在CPU上运行的进程所处的状态
    • 阻塞态:因缺少某种资源(例如IO资源,磁盘读写)正在等待的进程

    image-20231105230151777

    操作系统中这三个状态是理论的,在实际应用中的操作系统围绕这三个状态的核心状态延伸出了许多状态.接下来来看看Linux操作系统中的各种状态.

    1. 运行 “R (running)”
    2. 浅度睡眠 “S (sleeping)”
    3. 深度睡眠 “D (disk sleep)”
    4. 暂停 “T (stopped)”
    5. 僵尸 “Z (zombie)”
    6. 死亡 “X (dead)”

    1. 运行 >> R (running)

    顾名思义,为正在占用CPU运行的进程.也为操作系统中的运行态

    我们写一个这样的程序,让该进程陷入死循环,更方便观察.

    #include 
    int main()
    {
        int cnt = 0;
        while(1)
        {
            cnt++;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    gcc -o proce proce.c
    
    • 1

    运行起来后,我们用观察进程的ps指令看看

    ps -ajx | head -1 && ps -ajx | grep porce
    
    • 1

    可以观察到如下情况:

    image-20231105232021997

    其中STAT表示该进程的运行状态为R

    但这里并不是时时刻刻CPU都在运行这个进程的.CPU当中有一个 时间片 的概念,操作系统也维护了一个 运行队列,将所有R状态的进程都放在运行队列上.当一个时间片过后,就执行 进程切换(将进程从CPU上拿下来,换一个在运行队列当中的进程上去).

    这是一个并行的过程:在物理上是分段进行,在逻辑上是同时发生

    4a00b4c200024fd770aa18fd0e50da4

    2. 浅度睡眠 >> S (sleeping)

    为操作系统中的阻塞态,因在等待某种资源而无法被CPU调度

    将上方的程序进行修改,加入一个printf即可观察.因为CPU运行比IO快很多很多,所以需要IO的进程大多时间都在等待IO.可以观察到S状态

    #include 
    #include 
    int main()
    {    
        int cnt = 0;    
        while(1)    
        {        
            printf("hello\n");
            sleep(1);
        }    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    image-20231105234438109

    printf()实际上是封装了系统调用,能访问硬件资源.在进程中每个硬件资源都维护了一个等待队列.

    447731e52ec7482c0a1e5714cbdc4dd

    需要访问硬件资源的进程,大多时候都处在等待队列中.(因为CPU一下就处理完了)

    3. 深度睡眠 >> D (disk sleep)

    为操作系统中的阻塞态,因在等待磁盘资源而无法被CPU调度

    设想一个这样的场景:

    进程给磁盘发送了一个写入的指令,磁盘正在工作.此时进程正在等待磁盘给反馈信号(是否完成作业).突然操作系统内内存不足,需要释放一部分处在阻塞态的进程,这时若释放这种进程.那么磁盘写入丢失了怎么办呢?

    所以引入了一个特殊的阻塞态,该状态无法被操作系统调度.仅能根据磁盘反馈的信号做出相应动作(重写 or 停止).不响应任何其他请求

    4. 暂停 >> T (stopped)

    为操作系统中的阻塞态,这是一种主动让进程停止的状态.

    例如我们之前使用的gdb调试.程序运行到我们打的断点时,都相当于给进程发送了一个暂停信号.重新运行的时候再次发送一个继续信号.

    我们输入

    kill -l
    
    • 1

    查看kill所有信号

    image-20231106100318358

    其中18号命令 SIGCONT为继续信号, SIGSTOP为停止信号.

    我们运行之前能在屏幕上输出Hello的进程.对其发送停止信号

    kill -18 843951
    
    • 1

    image-20231106100816680

    可以直观的看到,提示进程843951被暂停了.我们再使用18号指令让进程继续运行

    kill -18 843951
    
    • 1

    image-20231106101516859

    5. 僵尸 >> Z (zombie)

    其为处于退出态的前一种状态

    一个人在路上跑步,突然间猝死了.警察来先调查原因,将原因告知家属,再通知处理尸体

    虽然例子有点抽象.但很形象.进程发出结束信号时,需要先将自己的结束状态告诉家属(父进程)是否正常结束等…

    此时进程处在的就是僵尸状态.

    编写以下代码创建一个进程,子进程比父进程更快回收.可以清楚的观察到僵尸

    #include 
    #include 
    #include 
    int main()
    {
        pid_t id = fork();
        if(id == 0)
        {
            int cnt = 10;
            while(cnt--)
            {
                printf("i am a child process my pid %d\n",getpid());
            }
            exit(0);
        }
        int cnt = 20;
        while(cnt--)
        {
            printf("i am a father process my pid %d\n",getpid());
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    image-20231106103451282

    当子进程先退出后处在僵尸状态,会等待父进程进行回收.父进程结束后,会自动回收子进程一起带走.

    那如果父进程比子进程先结束呢?

    上次我们说到 所有进程的父进程都是bash.所以一个进程成为了孤儿进程(父进程比子进程早结束),此时就会将自己的父进程转为bash.

    6. 进程优先级

    因为资源是有限的,进程是多个的.所以注定进程间具有优先级的概念.

    ps -alx | head -1 && ps -alx | grep porce
    
    • 1

    image-20231106104450230

    其中PRI与NI就是进程优先级与NICE值.

    有这样的计算公式: **PRI(新)=PRI(旧)+NICE **

    其中PRI旧永远都是从80开始,值越小优先级越大,NICE取值范围为[-20,19]

    可以在ROOT权限下使用top命令调整NICE值,进而改变某进程的优先级

    具体的为:先运行top,然后输入r即可调整

    在运行队列中,会维护两个优先级队列,一个为等待队列,一个为当前正在运行的队列.

    struct runqueue{
      bitmap isempty;
      task_struct **run;
      task_struct **wait;
      task_struct *running[140];
      task_struct *waiting[140];
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    r即可调整

    在运行队列中,会维护两个优先级队列,一个为等待队列,一个为当前正在运行的队列.

    struct runqueue{
      bitmap isempty;
      task_struct **run;
      task_struct **wait;
      task_struct *running[140];
      task_struct *waiting[140];
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其中run指针指向当前正在运行的队列,其按优先级进行分级.依次从下往上运行.
    在此过程中,若有新的进程加入运行队列,则会被放在等待队列中.
    当运行队列运行完毕时,则将run指针与wait指针互换.原运行队列变为等待队列,等待队列变为运行队列.

    image-20230905164632777

  • 相关阅读:
    基于Spring Boot的医院预约挂号系统设计与实现(源码+lw+部署文档+讲解等)
    个人游戏开发者的好时代
    涉及区间的查询
    缺少微信小程序测试经验?这篇文章带你从0开始
    Part03-DatabaseStorage i
    C# Winform内嵌窗体(在主窗体上显示子窗体)
    面试算法22:链表中环的入口节点(1)
    (三)admin-boot项目之整合alibaba-druid连接池
    kubernetes部署jenkins
    【全志V3s】SPI NAND Flash 驱动开发
  • 原文地址:https://blog.csdn.net/qq_62839589/article/details/134241705