Linux进程状态与进程优先级

news2024/9/25 16:48:14

目录

Linux进程状态与进程优先级

前置知识

并行与并发

时间片

进程状态

基本介绍

等待状态的本质

swap分区

Linux进程状态

Linux进程状态分类

运行状态(Running)与等待状态(Sleeping)

硬盘等待状态(Disk Sleeping)

停止状态(Stopped)

追踪停止状态(tracing stopped)

僵尸状态(Zombie)和终止状态(Dead)

进程退出

僵尸进程

孤儿进程

进程优先级


Linux进程状态与进程优先级

前置知识

并行与并发

并发:表示CPU在同一个时间内执行多个任务

并行:表示多个CPU在同一个时间内执行各自的任务

示意图如下:

时间片

时间片(timeslice),又称为“量子(quantum)”或“处理器片(processor slice)”,是分时操作系统分配给每个正在运行的进程微观上的一段CPU时间(在抢占内核中是:从进程开始运行直到被抢占的时间)

现代操作系统(如:Windows、Linux、Mac OS X等)允许同时运行多个进程。例如,在打开音乐播放器的同时用浏览器浏览网页并下载文件。由于一台计算机通常只有一个CPU,所以不可能真正地同时运行多个任务。这些进程「看起来像」同时运行,实则是轮番运行,由于时间片通常很短(在Linux上为5ms-800ms),用户基本不会感觉到。

时间片由操作系统内核的调度程序分配给每个进程。首先,内核会给每个进程分配相等的初始时间片,然后每个进程轮番地执行相应的时间,当所有进程都处于时间片耗尽的状态时,内核会重新为每个进程计算并分配时间片,如此往复。

在现代操作系统中,大部分的民用级操作系统均是分时操作系统,这类操作系统的最大特点就是可以通过多道程序和多任务处理的方式让用户感觉到「尽管只有一个CPU,但是应用可以同时执行」

  • 多道程序:表示操作系统能够同时管理多个运行中的程序。在早期的计算机系统中,一次只能运行一个程序。当这个程序结束或者因为某种原因暂停时,需要手工干预来加载下一个程序。而多道程序技术允许系统同时保持多个程序在内存中,并且这些程序可以交替执行,这样就提高了系统的利用率和效率
  • 多任务处理:表示操作系统能够在同一时刻处理多个任务的能力。在多任务环境下,操作系统通过快速地切换上下文(即保存当前任务的状态并加载新任务的状态),此处切换的时间依据就是时间片,使得多个任务看起来像是同时进行的一样

多道程序强调的是在一个系统中同时存在多个程序的能力,而多任务处理则进一步强调了这些程序能够以一种看似同时的方式执行

与分时操作系统类似的,就是实时操作系统,该类操作系统最大的特点就是如果有一个任务需要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。

进程状态

基本介绍

在操作系统中,一般会存在一个进程状态转换图,例如下图:

整个过程中涉及到五个基本进程状态:

  1. 创建(new):表示进程创建
  2. 运行(running):表示进程正在被执行
  3. 等待(waiting):表示进程正在等待具体事件发生,也被称为阻塞状态
  4. 就绪(ready):等待被调度器调度执行
  5. 终止(terminated):进程完成执行

执行过程如下:

  • 当进程创建成功后(new),其状态转化为就绪(ready),等待调度器调度(scheduler dispatch),调度到当前进程后开始运行(running),程序正常结束退出(exit)向操作系统返回数据,最后终止(terminated)
  • 整个过程中涉及到等待和中断,例如:当程序需要进行类似于IO或者其他事件(I/O or event wait)时就会进入等待状态,等待IO结束或者其他事件结束(I/O or event completion)再从等待转换为就绪状态(ready)等待调度器调度重新进入运行状态(running)

等待状态的本质

下面重点考虑等待(waiting)状态

进程在被创建之后,此时根据操作系统「先描述,再组织」的管理方式,在创建进程时,会形成对应进程的PCB(例如Linux下的task_struct),此时「描述」已经完成

接着程序进入就绪状态,此时操作系统会将进程对应的PCB加载到就绪队列中,在Linux下是一般是使用双向链表结构对每一个PCB进行连接,示意图如下,其中current指针表示当前正在被执行的进程:

每一个CPU需要执行进程,就需要一个与就绪队列有关的结构,该结构中存在当前进程的相关信息,例如进程状态等,一般结构中还会存在一个指针,该指针指向正在运行的进程,此时head指针指向的就是当前进程

如果此时程序需要进行I/O操作,因为I/O操作速度远小于CPU的执行速度,在分时操作系统中,会尽可能提高CPU的利用率,所以此时当前进程就会被操作系统切换到指定设备的等待队列(例如键盘),而CPU继续执行其他存在于就绪队列中的进程。等待队列与就绪队列结构基本一致,也是一个双向链表结构。进程进入等待队列中链接后,对应进程状态更改为等待状态,等待I/O操作完成。

等待队列和运行队列示意图如下:

当I/O操作完成,继续进入就绪队列等待被调度执行进入运行状态

综上所述:等待的本质就是进入对应设备的等待队列进行执行,只是不会执行对应的代码,而等待和运行的切换就是进程PCB在不同的双向链表结构中连接

swap分区

swap分区从字面意思上来看就是交换分区,该分区一般存在于硬盘中,主要用于内存和硬盘之间的资源交换,但是这种交换并不是常规性的,一般出现于内存空间严重不足的情况

当内存空间严重不足时,操作系统为了保证自身的运行正常,会将当前正在等待队列的进程对应的代码和数据放到硬盘的swap分区,尽可能减少内存空间的占用,这个过程也被称为「换出」,此时进程的状态也被称为阻塞挂起状态

而当执行到指定进程时,操作系统会重新将对应进程的代码和数据从swap分区加载到内存,从而达到正常运行的目的,这个过程也被称为「换入」

整个过程中的「换入」和「换出」实际上就是利用「时间换空间」的思想,因为swap分区在硬盘上,所以避免不了交换速度慢,如果出现大量的交换,整机的效率就被大大拉低

部分操作系统也会存在一个属于就绪队列的 swap分区,同样内存空间严重不足时,会将处于就绪队列中的部分进程的代码和数据进行换入和换出

Linux进程状态

前面操作系统的进程状态只是一个广泛的状态,每一种操作系统的进程状态可能不尽相同,下面主要谈Linux下的进程状态

Linux进程状态分类

在Linux下,进程状态被分为下面的7种:

  1. R(Running):运行状态
  2. S(Sleeping):可被中断的等待状态
  3. D(Disk Sleeping):不可被中断的等待状态
  4. T(Stopped):停止状态
  5. t(Tracing Stop):追踪停止状态
  6. X(Dead):终止状态
  7. Z(Zombie):僵尸状态,终止状态前的一种状态
在Linux下,就绪状态和运行状态一般不作区分,所以就绪队列也可以认为就是运行队列

运行状态(Running)与等待状态(Sleeping)

运行状态:表示程序正在就绪队列或者正在被CPU执行,包括前台运行和后台运行

在Linux下,通过 ps ajx查看到的状态代号后的 +代表正在前台运行,可以使用Ctrl+C终止,没有 +则表示后台运行,不可以使用Ctrl+C终止,只能使用 kill命令

例如,下面的C语言程序:

#include <stdio.h>

int main()
{
    while(1) {

    }
    return 0;
}

对应的Makefile如下:

TARGET=status
SRC=status.c

$(TARGET):$(SRC)
    gcc $^ -o $@

.PHONY:clean
clean:
    rm -f $(TARGET)

查看进程效果如下:

但是,需要注意,如果上面程序写为:

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

int main()
{
    while(1) {
        printf("hello\n");
        sleep(1);
    }
    return 0;
}

此时尽管程序在前台执行,查看进程时会显示S+,表示在前台等待:

之所以会出现这种情况,是因为printf函数本质是在做I/O,而因为I/O的速度远小于CPU的执行速度,所以为了保证CPU利用率,在做I/O的过程中,当前进程会被操作系统列入到等待队列,而CPU继续执行其他处于就绪队列的进程

硬盘等待状态(Disk Sleeping)

硬盘等待状态是Linux系统特有的进程状态,前面提到当内存空间严重不足时,操作系统为了保证自身在内存中的空间安全,会将部分处于等待队列的进程对应的代码和数据换入swap分区

假设在「内存空间严重不足」的背景下,内存中的某一个进程需要向硬盘写入非常多的数据,此时就会进行I/O操作,而正在做I/O的进程就处于等待队列中,而操作系统此时因为要保证自身安全,就会换出一部分进程的代码和数据到swap分区。

假设这个行为刚好将正在等待完成大量数据I/O的进程对应的代码和数据换入到了swap分区,当I/O设备向内存中指定的进程反馈相关信息(例如存储空间不足)时,由于该进程的相关代码和数据被换入到了swap分区,也就没有办法接受I/O的反馈信息,同时I/O设备也收不到后续的操作指令,这种情况下,就会出现因存储空间不足的问题导致数据丢失。

上面的过程中,如果数据是非常重要的数据,就会导致严重的损失

Linux系统为了防止这个问题的出现,提出了Disk Sleeping,该状态可以保证当内存空间严重不足时,该进程不会被操作系统换出

停止状态(Stopped)

依旧以上面C语言的代码为例:

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

int main()
{
    while(1) {
        printf("hello\n");
        sleep(1);
    }
    return 0;
}

kill指令中,存在两个选项:

代号为18的选项代表进程继续,代号为19的选项表示进程停止

在终端中输入:

// 停止进程
kill -19 进程PID

// 继续进程
kill -18 进程PID
需要注意,使用 kill -18 进程PID继续指定进程时,对应的进程状态代号后面不会带 +

就可以停止进程,即将指定进程的状态更改为Stopped

例如上面的程序,运行后执行kill -19 21827

想要程序继续运行,可以使用kill -18 21827

此时想终止程序,就必须使用kill -9 21827而不能使用Ctrl+C,停止进程后再按下Ctrl+C即可

追踪停止状态(tracing stopped)

对于追踪停止状态,可以在gdb调试指定代码时程序在断点位置暂停看到,例如调试前面的C语言代码,查看对应程序进程可以看到:

所以,调试代码之所以可以让程序停止运行,下一次还可以继续运行,本质就是通过追踪停止状态(tracing stopped)控制

僵尸状态(Zombie)和终止状态(Dead)

每一个进程需要执行都需要管理者的调度,但是进程是否结束管理者也需要知道,这里管理者有操作系统和其父进程,而进程告诉操作系统或其父进程自己正常结束的方式就是通过进程的退出信息,一般退出信息存在进程退出码,0表示进程正常退出,而这一过程发生时刻所处的状态就是僵尸状态

当操作系统或父进程通过某种方式获取了对应的进程的退出信息(例如进程退出时的退出码),进程状态就会变为终止状态,但是如果一直不查看进程退出信息,进程会一直处于僵尸状态

可以使用echo $?显示最近一次进程退出的信息,使用其查看ls命令在无法找到文件时的返回值以及找到文件时的返回值:

  • 未找到文件时

  • 找到文件时:

这里使用 echo $?查看进程退出码本质就是因为 bashls命令进程的父进程

这也就可以解释为什么之前在写C语言程序时,需要在主函数退出前写上return 0,这里的0就是告诉操作系统或其父进程当前进程正常退出

进程正常退出不一定程序完成了指定的任务,后面会细讲如何通过返回值判断进程是否完成任务

进程退出

进程退出:表示当前进程已经进入了僵尸状态,但不一定进入了终止状态

在Linux中,进程退出的特点是:保留对应进程的PCB,但是会销毁对应进程的代码和数据,而之所以要保留PCB就是因为进程的退出信息依旧存在于对应进程的PCB中,而保留的PCB就会被操作系统管理,方便未来查看

在Linux 1.0的源码中,可以看到部分退出信息,例如退出码和退出信号:

僵尸进程

僵尸进程就是处于僵尸状态的进程,前面提到如果操作系统或者父进程没有获取对应(子)进程的退出信息,该进程就会一直处于僵尸状态

例如下面的代码:

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

int main()
{
    printf("I am parent process, mypid: %d, myppid: %d\n", getpid(), getppid());

    // 创建子进程
    pid_t id = fork();

    if (id == 0) {
        while(1) {
            printf("I am a child process, my pid = %d, my ppid = %d\n", getpid(), getppid());
            sleep(2);
        }
    }

    // 父进程不接收子进程的退出信息
    while (1) {

    }

    return 0;
}

编译运行上面的代码,再结束掉对应的子进程可以看到下面的信息:

其中,PID为26544的为父进程,PID为26545的为子进程,在上面的代码中,结束子进程后,父进程并没有对子进程的退出信息进行接收,所以此时子进程就会持续保持僵尸状态,并且对应的进程会被修饰为<defunct>,表示「失效的」,此时的task_struct就会被操作系统保存,但是对应进程的代码和数据就会被操作系统移除

因为处于僵尸状态时,进程已经退出,所以不可以再使用kill指令结束该僵尸进程:

进入僵尸状态的进程,默认情况下是不会被任何进程托管,所以一旦出现了僵尸进程,就表示该进程退出信息没有任何进程接收,这种情况下就会出现内存泄漏问题

在前面C/C++语言层面提到的内存泄漏表示开辟的空间在没有使用的情况下,程序运行时没有释放导致持续占用空间,但是这种内存泄漏最大的特点就是程序一旦结束,该空间就会被释放。所以语言层面的内存泄漏在常驻内存的进程上影响最大,但是不论如何,还是要处理这种内存泄漏问题

此处进程的内存泄漏表示处于僵尸状态的进程,因为进程退出信息没有被接受,导致其task_struct一直存在于内存中,但是这种内存泄漏是无法在程序结束后被操作系统自动释放。所以为了避免出现这种内存泄漏问题,需要对每一个进程的退出信息进行接收

孤儿进程

前面提到的是子进程先结束,父进程没有结束并且不接受子进程的退出信息,子进程就处于僵尸状态,如果反过来先结束父进程,再结束子进程就会出现子进程变为孤儿进程,编译运行前面的代码,结束对应父进程结果如下:

孤儿进程最大的特点就是其PPID变为1,并且为后台运行,所以不可以使用Ctrl+C终止,可以使用top指令查看PID为1对应的进程:

根据上面的结果,可以看到孤儿进程会被系统托管

进程优先级

进程优先级,表示优先被CPU执行的进程的等级,在Linux下,进程优先级等级越小,优先级越高,被优先执行的概率越大

之所以需要进程优先级,是因为大部分的民用电脑都只有一个CPU,但是进程的个数可以有很多,这种情况下就需要进程对CPU资源的抢夺,为了保证部分进程能以更大优势抢到CPU资源,就需要进程优先级

在Linux下,可以使用下面的指令查看到当前用户执行的进程对应的优先级:

ps -la

在Linux下,进程优先级由两个值进行控制,一个是PRI(priority),另一个是NI(nice),PRI代表进程启动时系统自动分配的优先级,而NI代表优先级修正值,这个值的范围是[-20, 19]

在计算Linux进程的优先级时,使用公式:PRI = 初始PRI + NI

例如,启动下面的C语言程序:

#include <stdio.h>

int main()
{
    while(1) {

    }
    return 0;
}

使用ps -la查看效果:

因为初始的PRI为80,NI为0,所以最终的PRI = 80 + 0 = 80

在Linux下,不可以修改PRI,但是可以通过修改NI从而改变进程优先级。使用top指令,再输入r,再输入对应的NI值即可修改

如果将NI修正为-6,则会出现下面的结果:

因为默认的PRI值为80,而此时NI值为-6,所以最终的PRI = 80 - 6 = 74

如果此时将NI修正为15,则会出现下面的结果:

默认的PRI值为80,而此时的NI值为15,所以最终的PRI = 80 + 15 = 95

可以看到,尽管开始修改了PRI为74,下一次再更改NI值时,计算PRI使用的还是初始的PRINI

进程优先级并不支持频繁修改,在Linux下,可能修改1次或者2次左右后再修改 NI就需要使用 root权限

在实际中,进程优先级一般很少去修改,尽管可以在程序中使用函数更改进程优先级或者使用命令修改进程优先级

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机视觉必备模型YOLO系列模型的知识点,提供YOLOv1-v8模型结构与代码实例

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉必备模型YOLO系列模型的知识点&#xff0c;提供YOLOv1-v8模型结构与代码实例。本文全面介绍了计算机视觉领域中必备的YOLO系列模型&#xff0c;详细梳理了YOLOv1至YOLOv8模型的结构及其演变过程。文章内容…

Node.js 学习 path模块、fs模块、npm软件包管理器、导出、导入

目录 1.Node.js入门 1.1 什么是 Node.js 1.2 fs模块-读写文件 1.3 path模块-路径处理 1.4 案例-压缩前端html 1.5 认识URL中的端口号 1.6 http模块-创建Web服务 1.7 案例-浏览时钟 2.Node.js 模块化 2.1 模块化简介 2.1.1 什么是模块化&#xff1f; 2.1.2 CommonJS…

【软件建设方案】设备管理系统建设方案(Doc原件参考)

1.系统概述 1.1.需求描述 1.2.需求分析 1.3.重难点分析 1.4.重难点解决措施 2.系统架构设计 2.1.系统架构图 2.2.关键技术 3.系统功能设计 3.1.功能清单列表 3.2.设备信息数据库 3.3.设备维护计划管理子系统 3.4.设备维护管理子系统 3.5.备件物资管理子系统 3.6.…

1区IEEE-Trans发文暴涨3倍,CCF-B类,刚跌出了TOP榜!这是不是官方提前发出警告?

【SciencePub学术】今天给大家推荐的是1本地球科学领域的SCI—《IEEE TRANSACTIONS ON GEOSCIENCE AND REMOTE SENSING》&#xff0c;IEEE-Trans系列&#xff0c;并且是CCF-B类期刊&#xff01;此系列的期刊在业界的权威性还是不容置疑的。 优点VS缺点 ✦ IEEE-Trans系列 ✦ C…

普通本科生也能成为AI高手:人工智能学习指南

在人工智能&#xff08;AI&#xff09;日益普及的今天&#xff0c;许多人都渴望掌握这项技术&#xff0c;但常有人疑惑&#xff1a;没有顶尖学府的背景&#xff0c;我也能学习人工智能吗&#xff1f; 答案是肯定的&#xff01; 人工智能是一个充满机遇与挑战的领域&#xff0c…

渗透测试工具 sqlmap 基础教程

一、引言 在网络安全领域&#xff0c;渗透测试是一项至关重要的工作&#xff0c;它可以帮助我们发现系统中的安全漏洞&#xff0c;从而采取相应的措施进行修复。而 sqlmap 作为一款强大的开源渗透测试工具&#xff0c;专门用于检测和利用 SQL 注入漏洞。本文将为大家详细介绍 …

HTTP 1.0 2.0 3.0详解

HTTP HTTP全称超文本传输协议&#xff0c;是一种属于应用层的通信协议。它允许将超文本标记语言文档&#xff08;HTML&#xff09;从Web服务器传输到客户端的浏览器。 HTTP报文结构 请求报文结构 请求方法&#xff1a; GET&#xff1a;一般用来请求已被URI识别的资源&#x…

Elasticsearch7.7.1集群不能相互发现的问题解决以及Elasticsearch7.7.1安装analysis-ik中文分词插件的应用

一、Elasticsearch7.7.1集群不能相互发现的问题解决 在使用elasticsearch7.7.1搭建集群&#xff0c;使用了3台服务器作为节点&#xff0c;但在搭建的过程中发现每台服务器的elasticsearch服务都正常&#xff0c;但是不能相互发现&#xff0c;期间进行了一些配置的修改偶尔出现了…

(附源码)SSM养老院综合服务管理系统-计算机毕设 23237

基于SSM的养老院综合服务管理系统 摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;…

助力解析化学图像生成文本分析,化学大语言模型 ChemVLM 来啦!

ChemVLM 是由上海人工智能实验室于 2024 年推出的首个面向化学领域的开源多模态大型语言模型。该模型旨在解决化学图像理解与文本分析之间的不兼容问题&#xff0c;通过结合视觉 Transformer (ViT)、多层感知机 (MLP) 和大型语言模型 (LLM) 的优势&#xff0c;实现了对化学图像…

万维组态介绍

演示地址&#xff1a;http://121.40.16.189:12000 万维组态本地部署文档万维组态线上部署文档万维组态操作说明文档万维组态接入文档万维组态绑点示例文档万维组态接入源代码说明万维组态扩展图元示例文档万维组态大屏图元示例文档 项目介绍 万维组态是一款功能强大的基于Web的…

sar信号RD域的距离向傅里叶变换

下面可知&#xff0c;举例傅里叶变换时&#xff0c;posp 距离时间和频率 t不等于ft/K。而方位时间和频率时这种线性关系

整合SpringSecurity框架经典报错

报错描述Description: Field userDetailsService in com.atguigu.security.config.WebSecurityConfig required a bean of type org.springframe 这是整合SpringSecurity权限认证中经常出现的一个问题&#xff0c;由于SpringSecurity中这个UserDetailsService未找到 解决方案…

稀疏线性方程组求解技术——超节点法(Supernodal)简介

一、介绍 直接法的基础是矩阵的分解&#xff0c;常见的分解形式有LU分解、Cholesky分解、LDL分解等。 直接法通过将A矩阵分解成两个或多个因子的乘积&#xff0c;使得原方程组转化为若干个较容易求解的子问题。例如LU分解ALU&#xff0c;其中L是单位下三角矩阵&#xff0c;U是…

JavaScript typeof运算符

在js中可以typeof来做到确定一个值到底是什么类型。 <script>var num 100;//数值类型var name "mingzi";//字符串类型var book true;//布尔类型var student {name: " 小明",age: 16,tnum: "213444"}//对象是由多个数据组合而成&#x…

效率工具推荐 | 高效管理客服中心知识库

人工智能AI的广泛应用&#xff0c;令AI知识库管理已成为优化客服中心运营的核心策略之一。一个高效、易用且持续更新的知识库不仅能显著提升客服代表的工作效率&#xff0c;还能极大提升客户的服务体验。而高效效率工具如HelpLook&#xff0c;能够轻松搭建AI客服帮助中心&#…

[论文速读] Multimodal Fusion on Low-quality Data:A Comprehensive Survey 低质多模态数据融合综述

摘要&#xff1a; 多模态融合侧重于整合多种模态的信息&#xff0c;以实现更准确的预测&#xff0c;在自动驾驶和医疗诊断等广泛场景中取得了显着进展。然而&#xff0c;多模态融合的可靠性在很大程度上仍未得到探索&#xff0c;特别是在低质量数据设置下。本文调查了野外多模态…

Android平台使用VIA创建语音交互应用

Android平台使用VIA创建语音交互应用 概述 在 Android 平台上开发一款语音助手应用需要整合多种技术,包括语音识别(ASR)、文字转语音(TTS)、以及热词检测(Hotword Detection)。这些技术共同构成了语音助手应用的核心交互方式,使用户能够通过语音命令与设备进行无缝交…

JavaWeb便利店管理系统

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 spring-mybatis.xml3.5 spring-mvc.xml3.5 login.jsp 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优…

spring里面内置的非常实用的工具

一 、请求数据记录 Spring Boot提供了一个内置的日志记录解决方案&#xff0c;通过 AbstractRequestLoggingFilter 可以记录请求的详细信息。 AbstractRequestLoggingFilter 有两个不同的实现类&#xff0c;我们常用的是 CommonsRequestLoggingFilter。 通过 CommonsRequestL…