Linux 进程状态

操作系统学科的进程状态

请添加图片描述

  • 新建态:刚刚创建的进程, 操作系统还未把它加入可执行进程组, 它通常是进程控制块已经创建但还未加载到内存中的新进程。
  • 就绪态:进程做好了准备,只要有机会就开始执行。
  • 阻塞态:进程在某些事件发生前不能执行,如 I/O 操作完成。
  • 执行/运行态:进程正在执行,假设计算机中只有一个处理器,因此最多只有一个进程处于这个状态。
  • 终止/退出态:操作系统从可执行进程组中释放出的进程,要么它自身已停止,要么它因某种原因被取消。

上面的就是操作系统学科中,可能会提到的进程状态!当然你还可能看到诸如:就绪挂起,阻塞挂起等概念!
我们要学习的是一款具体的操作系统:linux 操作系统对进程状态的定义和实现。

linux 进程状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 Linux 内核里,进程有时候也叫做任务)。
下面的状态在 linux 内核源代码里定义:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
  • R 运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
  • S 睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
    (interruptible sleep))
  • D 磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
  • T 停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
  • X 死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

运行状态,R

在 linux 中,进程控制块 task_struct 是用双向链表链接起来的!操作系统维护了一个运行队列,凡是在运行队列中的进程就都处于运行态!被放在操作系统维护的运行队列中的是进程的控制块,即 task_struct,当轮到某个进程的代码被 cpu 执行时,我们能够通过运行队列中的 task_strcut 找到该进程对应的代码和数据!然后开始执行!
在这里插入图片描述
一个正在 cpu 上运行的进程是不是一直要等到该进程的代码执行完毕才把自己从 cpu 上扒下来呢?显然这是不可能的!每一个进程都有一个叫做时间片的概念,当某个进程的时间片消耗完了,就会脱离 cpu,换下一个进程到 cpu 上执行!由一个进程切换到另一个进程,叫做进程切换
linux 中进程的时间片大约是:5~800ms,这就意味着一个进程每次在 cpu 上执行的时间是有限的!加上 cpu 来回地切换进程!我们就能够看到多个进程在同一时间同时运行的现象!


我们能够尝试看到运行状态嘛?因为每个进程在 cpu 上执行的时间都非常短,看到这个状态也是不容易的!

  • 首先我们得学会查看进程的状态嘛!在进程的概念部分我们讲解了查看进程部分属性的方法,其中 ps axj 就能查看进程的状态!

  • 写程序:

    #include<stdio.h>
    
    int main()
    {
    	while(1) printf("hello linux\n");
    	return 0;
    }
    //下面是 makefile 文件
    test:test.c
    	gcc -o $@ $^ -std=c99
    .PHONY:clean
    	clean:
    rm -f test
    

    我们一直在循环打印 “hello linux”。

  • 通过监控脚本查看可执行程序(进程)的状态:

    while :; do ps axj | head -1 && ps axj | grep test | grep -v grep; echo "-------------------------------------------------"; sleep 1; done;
    

    在这里插入图片描述
    我们看到尽管显示器上一直在打印 “hello linux” 但是我们看到的进程状态依旧是:S+,即阻塞状态!
    这个 S+S 的区别就是:

    • S+:加号代表前台进程,S+ 就是这个前台进程正处于阻塞状态。
    • S:表示后台进程正在阻塞中!

    这里前后台的概念就跟手机上的前后台概念差不多!
    我们运行可执行程序的时候加上 & 就能在后台运行啦:./test &
    在这里插入图片描述
    这个时候我们就能看到:S 而不是 S+。在后台运行之后,你发现使用:ctrl + c 结束不掉这个进程了!那是因为 ctrl + c 只能结束前台进程!那要结束该怎么办呢?我们可以使用信号(信号我们后面会详细讲解):

    kill -9 1322154
    

    看上图,这个进程的 pid 是:1322154。我们给 pid 为 1322154 的进程发送九号信号就能将这个进程给杀掉了!

可这并不符合我们的预期哇!我们想看到的是 R 状态哇!怎么办呢?我们尝试将 printf 去掉试试!

#include<stdio.h>

int main()
{
    while(1) ;
    return 0;
}

在这里插入图片描述
我们看到也是成功看到 R 状态啦!这是因为加上 printf 这个进程会等待显示器资源就绪,从而使得进程大部分时间处于阻塞状态!主要是 cpu 执行的速度太快了!

阻塞状态,S

操作系统中为正在运行的进程维护了运行队列!同样也会为正在阻塞的进程维护阻塞队列!一个进程处于阻塞状态的场景很多,比如:等待外设资源就绪(包括,键盘,网卡,鼠标,显示器等等)。
操作系统中的大部分进程都是处于阻塞状态的!


下面的代码中,正在等待键盘的输入,那么这个进程的状态就是阻塞状态呢!

#include<stdio.h>

int main()
{
    int a;
    scanf("%d", &a);
    return 0;
}

在这里插入图片描述

深度睡眠/磁盘休眠,D

我们假设一个场景:在很久很久以前,有一个进程 A,正在向磁盘中写入数据!进程 A 正在欢快的写着。突然,操作系统发现,自己的内存空间严重不足了!当他路过进程 A 身边时,发现 A 正在向磁盘中慢吞吞的些数据呢!操作系统看他不顺眼,直接将进程 A 给他杀掉了!(操作系统为了保证自己的正常运行,完全可能杀进程的,类比手机开很多应用程序,操作系统杀后台的例子)。磁盘正写着呢,突然发现这个进程没了,这数据才写道一半呢?怎么办呢?磁盘心想,不完整的数据还是丢了吧!(这是大部分情况的结果!)。
这个时候上层用户发现对自己非常重要的数据没了!一纸诉状,将操作系统,进程,磁盘一并告上了法庭!请听被告的辩词:

  • 操作系统:亲爱的用户,您赋予我管理软硬件资源的权力,为的就是向您提供一个安全,流畅,的运行环境!当时我正处于危机时刻,如果不通过杀死进程来释放内存,我就会崩溃的!导致电脑直接挂掉!
  • 磁盘:我一直在认真完成自己的工作哇,数据写到一半,进程突然没了!我只能将残缺的数据丢掉了!因为我的设定本就是如此!如果我有罪,那么其他的磁盘是不是也同样有罪!
  • 进程:我可是受害者哇!我是被杀掉的,怎么能有罪呢?

如果您是法官大人,您觉得是谁的错呢?显然他们都没有错!
从那以后,当进程正在向磁盘中写数据的时候,他的状态就会被修改为 D 状态,这个 D 状态就是一块免死金牌,操作系统无法杀掉这个进程!
操作系统中 D 状态的进程很短,很少,用户一般都察觉不到。我们无法演示出来 D 状态!
如果真被用户察觉到,那么操作系统肯定要挂了!

暂停状态,T(t)

我们目前认为:Tt 状态没有区别!
在进程状态中的 R 状态中,我们学会了用 kill 命令杀掉一个进程!现在我们再来学习如何使用 kill 命令暂停和运行一个进程!

kill -l #列出所有信号

在这里插入图片描述

(信号部分的原理我们后期会详细讲解,这里学会怎么使用就行啦)这里面有两个信号:SIGSTOPSIGCONT 分别用来暂停,和运行一个暂停的进程!一个进程被暂停之后,我们就能看到 T 状态的进程啦!

#include<stdio.h>
#include<unistd.h>

int main()
{
    for(int i = 0; ; i++)
    {
        printf("hello linux: %d, pid: %d\n", i, getpid());
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
我们看到,在给 pid 为 1613503 的进程发送 19 号信号之后,进程就暂停下来了,查看这个进程的状态就是 T 状态!
我们在给他发送 18 号信号就能让他从暂停状态变为运行状态!
在这里插入图片描述
T 状态与 S 状态有区别,T 状态可以理解为阻塞状态,只不过 T 状态可能是控制一个进程,或者等待!


T/t 状态有什么运用场景嘛?我们之前是不是学习过 gdb,让程序在代码的某个位置停下来(调试),不就是 T/t 状态的运用嘛!
在这里插入图片描述

终止态,X

终止态的进程是真的就看不到了,一个进程死亡了,被放入垃圾回收的队列中,回收资源!
终止态是一个瞬时状态!

僵尸状态,Z

一个进程退出了,进程的退出信息会被维护一段时间,父进程获取了进程的退出状态之后,该进程由 Z 状态变为 X 状态
进程退出信息被维护的这一段时间,进程就处于 Z 状态!
一般情况下,进程退出的时候,如果父进程没有主动回收子进程的退出消息,子进程会一直处于 Z 状态,子进程相关的资源,尤其是 task_struct 结构体不能被释放,那么子进程的 task_struct 就会一直占用内存资源,造成内存泄漏。

父进程获取子进程退出信息的过程称为进程等待!这个知识点后面会详解!
父进程直接创建了子进程嘛,子进程要退出了,父进程肯定要关心关心撒!父进程创建子进程就是让子进程来办事的嘛!

我们可以写一个代码来验证,子进程退出的时候,父进程不获取子进程的退出状态,那么子进程就会处于僵尸状态的结论:让子进程循环打印 pidppid 三秒之后退出循环,退出循环之后结束子进程。父进程打印自己的 pid

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>

int main()
{
    pid_t id = fork();
    if(id == 0) //子进程
    {
        int cnt = 0;
        while(1)
        {
            printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
            sleep(1);
            cnt++;
            if(cnt == 3)
                break;
        }
        exit(0);
    }
    else if(id > 0)
    {
        while(1)
        {
            printf("I am parent, pid: %d\n", getpid());
            sleep(1);
        }
    }
    else
    {
        perror("fork(): ");
    }
    return 0;
}

使用监控脚本监控进程的状态:
在这里插入图片描述
我们看到在子进程退出之后,子进程的状态变成了 Z 状态,也就是这个进程变成了僵尸进程
至于怎么获取子进程的退出信息,我们会在进程等待的章节详细讲解!


你有没有想过,如果我们的父进程先退出,结果又是怎么样的呢?

孤儿进程

好的,我们就来写代码看看父进程比子进程先退出是怎么个事儿!
我们让父进程运行三秒之后退出,子进程一直运行,通过监控监本看看会出现什么现象!

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>

int main()
{
    pid_t id = fork();
    if(id == 0) //子进程
    {
        while(1)
        {
            printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
            sleep(1);
        }
    }
    else if(id > 0)
    {
        int cnt = 0;
        while(1)
        {
            printf("I am parent, pid: %d\n", getpid());
            sleep(1);
            cnt++;
            if(cnt == 3)
                break;
        }
    }
    else
    {
        perror("fork(): ");
    }
    return 0;
}

我们看到,父进程退出之后子进程的确一直再跑,只不过子进程的父进程好像发生了变化!
在这里插入图片描述
子进程的父进程变成了 1,我们来看看这个 1 号进程是啥啊!
在这里插入图片描述
一号进程是:systemd,不就是操作系统嘛!
这个现象说明:父进程先于子进程退出,这样的子进程被称为孤儿进程!孤儿进程会被操作系统领养!


为什么操作系统要领养他呢?
因为孤儿进程也要释放资源哇,之前是通过父进程获取子进程的退出状态之后,由操作系统释放资源!父进程提前退出了,那么就直接让操作系统释放不就行了!操作系统有这个能力做到!


bash 进程不是我们自己写的进程的父进程吗? bash 进程能领养孤儿进程嘛?
说到底就是 bash 没有这个能力,即使 bash 进程领养了子进程,但是系统中没有爷爷进程等待孙子进程的逻辑,即使领养了也没用。但是操作系统就不一样了,操作系统是所有进程的管理者,能够直接在内核层面回收!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/213939.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【富文本编辑器】原生JS使用WangEditor和vue上传图片前后端demo

【富文本编辑器】原生JS使用WangEditor上传图片前后端demo 第一步 HTML 第二步 初始化WangEditor与图片上传回调函数 第三步 后端返回数据体封装 第四步 后端接口上传图片&#xff0c;并返回图片地址 最近&#xff0c;我遇到了这样一个问题&#xff1a;因为我们的项目是基于…

网络和Linux网络_9(应用层和传输层_笔试选择题)

目录 一. 常见应用协议等等 1. 以下不是合法HTTP请求方法的是( ) 2. 文件传输使用的协议是&#xff08;&#xff09; 3. HTTP1.1的请求方法不包括&#xff1f;() 4. http状态码中&#xff0c;( )表示访问成功&#xff0c;( )表示坏请求&#xff0c;( )表示服务不可用。() …

【力扣206】反转链表

【力扣206】反转链表 一.题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1 &#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2 &#xff1a; 输入&#xff1a;head [1,2] 输出&#x…

物奇平台电容触摸功能调试

是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,+群赠送语音信号处理降噪算法,蓝牙耳机音频,DSP音频项目核心开发资料, 物奇平台电容触摸功能调试 1 修改按键驱动宏 2 编译生成wpk 文件,import 导入烧录文件。…

先遗忘后学习:基于参数计算的大模型知识更新

深度学习自然语言处理 原创作者&#xff1a;陈定纬编辑&#xff1a;cola 最近&#xff0c;大型语言模型&#xff08;LLMs&#xff09;展示了其令人惊叹的文本理解和生成能力。然而&#xff0c;即使是更为强大的LLMs&#xff0c;仍有可能从训练语料库中学到不正确的知识&#xf…

k8s安装学习环境

目录 环境准备 配置hosts 关闭防火墙 关闭交换分区 调整swappiness参数 关闭setlinux Ipv4转发 时钟同步 安装Docker 配置Yum源 安装 配置 启动 日志 安装k8s 配置Yum源 Master节点 安装 初始化 配置kubectl 部署CNI网络插件 Node节点 检查 环境准备 准…

LASSO vs GridSearchCV

LASSO VS GridSearchCV LASSO定义目的使用方法原理示例总结 GridSearchCV定义目的使用方法原理网格搜索&#xff08;Grid Search&#xff09;交叉验证&#xff08;Cross-Validation&#xff09;总结 示例总结 总结 LASSO 定义 LASSO&#xff08;Least Absolute Shrinkage and…

【ArcGIS Pro微课1000例】0040:ArcGIS Pro创建北极点、南极点

文章目录 一、创建北极点图层二、创建北极点三、不同投影系下北极点的位置一、创建北极点图层 选择一个数据库,在上面右键→新建→要素类。 输入名称:北极点。 空间参考:WGS 1984 点击创建。 二、创建北极点 在编辑选项卡下,点击【创建】。 在创建要素窗口中,点击北极点…

【RT-DETR改进】InnerIoU思想结合传统 EIoU、SIoU、WIoU损失思想(小目标涨点效果明显)

论文地址&#xff1a;官方Inner-IoU论文地址点击即可跳转 官方代码地址&#xff1a;官方代码地址-官方只放出了两种结合方式CIoU、SIoU 本位改进地址&#xff1a; 文末提供完整代码块-包括InnerEIoU、InnerCIoU、InnerDIoU等七种结合方式和其AlphaIoU变种结合起来可以达到二十…

轻盈悦耳的运动型气传导耳机,还有条夜跑灯,哈氪聆光体验

我平时出门不管是散步、骑行&#xff0c;还是坐公交的时候&#xff0c;都喜欢戴上耳机听音乐&#xff0c;这可以让我放松心情。现在市面上的耳机还是以真无线为主&#xff0c;选择虽多&#xff0c;但不适合户外使用&#xff0c;听不见外界的声音&#xff0c;运动时还容易脱落&a…

牛客在线编程(SQL大厂面试真题)

1.各个视频的平均完播率_牛客题霸_牛客网 ROP TABLE IF EXISTS tb_user_video_log, tb_video_info; CREATE TABLE tb_user_video_log (id INT PRIMARY KEY AUTO_INCREMENT COMMENT 自增ID,uid INT NOT NULL COMMENT 用户ID,video_id INT NOT NULL COMMENT 视频ID,start_time d…

链表【1】

文章目录 &#x1f348;2. 两数相加&#x1f34c;1. 题目&#x1f34f;2. 算法原理&#x1f353;3. 代码实现 &#x1f349;445. 两数相加 II&#x1f34d;1. 题目&#x1f350;2. 算法原理&#x1fad0;3. 代码实现 &#x1f348;2. 两数相加 &#x1f34c;1. 题目 题目链接&…

【数据结构高阶】AVL树

上期博客我们讲解了set/multiset/map/multimap的使用&#xff0c;下面我们来深入到底层&#xff0c;讲解其内部结构&#xff1a; 目录 一、AVL树的概念 二、AVL树的实现 2.1 节点的定义 2.2 数据的插入 2.2.1 平衡因子的调整 2.2.1.1 调整平衡因子的规律 2.2.2 子树的旋…

对一个多维随机变量作为线性变换以后的协方差矩阵

假设是一个n维的随机变量&#xff0c;它的协方差矩阵 对做线性变换&#xff0c;其中是一个矩阵&#xff08;当然也可以是一个标量&#xff09;&#xff0c;的协方差矩阵 证明如下&#xff1a; 将代入&#xff0c;得

git如何关联克隆远程仓库

一、添加远程仓库 之前我们仅仅是在本地创建了一个Git本地仓库&#xff0c;这里我们再在GitHub创建一个Git远程仓库&#xff0c;并且让这两个仓库进行远程同步&#xff0c;这样&#xff0c;GitHub上的仓库既可以作为备份&#xff0c;又可以让其他人通过该仓库来协作开发。 1.…

【无标题】我们只能用成功来摧毁我们,原来的自己只会破败自己的事情。

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

JavaWeb 添加页面和用户图像展示

add.jsp&#xff08;需要登录之后才可以访问 &#xff09; -> 不是和login.jsp同级了那就 在images目录下加上默认图像 js目录下加入common.js javaWeb项目中&#xff0c;页面的路径 img的src form的action link的href script的src a的href推荐使用绝对路径 这个绝对路径…

【海思SS528 | VO】MPP媒体处理软件V5.0 | 视频输出模块——学习笔记

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

Project 1: The Game of Hog(CS61A)

&#xff08;第一阶段&#xff09;问题 5a&#xff08;3 分&#xff09; 实现该函数&#xff0c;该函数模拟了完整的 Hog 游戏。球员 交替轮流掷骰子&#xff0c;直到其中一名玩家达到分数。playgoal 您现在可以忽略 Feral Hogs 规则和论点; 您将在问题 5b 中实现它。feral_h…

(学习笔记)Xposed模块编写(一)

前提&#xff1a;需要已经安装Xposed Installer 1. 新建一个AS项目 并把MainActvity和activity_main.xml这两个文件删掉&#xff0c;然后在AndriodManifest.xml中去掉这个Activity的声明 2. 在settings.gralde文件中加上阿里云的仓库地址&#xff0c;否则Xposed依赖无法下载 m…