【从浅学到熟知Linux】进程状态与进程优先级(含进程R/S/T/t/D/X/Z状态介绍、僵尸进程、孤儿进程、使用top及renice调整进程优先级)

news2025/6/23 21:36:06

在这里插入图片描述

🏠关于专栏:Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程及数据库等内容。
🎯每天努力一点点,技术变化看得见

文章目录

  • 进程状态
    • 进程状态查看
    • R运行状态(running)
    • S睡眠状态(sleeping)
    • D磁盘休眠状态(Disk sleep)
    • T停止状态(stopped)
    • t调试/追踪状态(tracing stop)
    • X死亡状态/终止状态(dead)
    • Z僵尸状态/僵尸进程(zombie)
  • 孤儿进程
  • 进程优先级
    • 基本概念
    • 查看系统进程
    • PRI及NI
    • 修改进程优先级
      • top命令
      • renice命令
    • 关于进程的相关概念


进程状态

关于进程状态,我们来看看Linux源代码中总共有几种状态↓↓↓

static const char* cinst task_state_array[] = {
"R(runnning)",/* 0 */
"S(sleeping)",/* 1 */
"D(disk sleep)", /* 2 */
"T(stopped)",/* 3 */
"t(tracing stop)",/* 4 */
"X(dead)", /* 16 */
"Z(zombie)" /* 32 */
};

在介绍这些状态时,将使用使用描述+代码验证的方式(但部分状态无法使用代码验证)。在开始介绍前,我们需要了解如何查看进程状态↓↓↓

进程状态查看

ps aux / ps axj

查看进程状态总共有两种方式,分别是ps auxps axj。使用它们查看进程的效果如下图所示↓↓↓
在这里插入图片描述
★ps:关于ps命令的更多用法,可以查询man手册。

R运行状态(running)

我们在创建了进程之后,操作系统会给该进程创建一个task_struct结构体,该结构体中包含进程状态、pid、ppid、优先级等字段,该结构体就是PCB(进程控制块),用于记录进程各类信息。

管理好这些进程,我们需要只要管理好task_struct结构体即可。因此,操作系统将task_struct链成一个链表(队列)。
在这里插入图片描述
由于计算机中的各类资源(包括CPU、内存、外部设备等)均十分宝贵,各个进程在获取某个资源时,可能需要到某个资源上排队。而等待CPU资源的进程将被链成一个队列,这个队列叫做运行队列(run_queue)。而处于运行队列上的进程的状态就是运行状态,即R状态

下面,我们编写一个死循环,并查看该进程的状态↓↓↓

#include <stdio.h>
int main()
{
	while(1)
	{}
	return 0;
}

在这里插入图片描述
在这里插入图片描述
上面的STAT的R就是运行状态。

S睡眠状态(sleeping)

如果我们编写一个循环打印"hello world"的程序,则执行该程序的进程的状态是R状态吗?

#include <stdio.h>
int main()
{
	while(1)
	{
		printf("hello world\n");
	}
	retrun 0;
}

在这里插入图片描述

这里我执行ps axj | head -1 && ps axj | grep test命令,得到的进程状态是S状态,即睡眠状态。这是为什么呢?那什么是睡眠状态呢?

我们在执行上面的程序时,进程需要访问外设,而外设相比与cpu而言,速度非常慢。该进程为了打印"hello world",它需要到对应的外设上等待(这里的外设是显示器),在它需要使用外设资源时,cpu将该进程的PCB从运行队列中取下来,并链入显示器的等待队列中。在除了cpu以外的队列中等待时,这时的状态就是S休眠状态。等该进程打印完毕后,再链入cpu,继续向下执行。但由于上面的程序频繁访问外设,导致它在运行队列中的时间非常短。因而,我们在查看该进程状态时,绝大多数情况下,它都处于睡眠状态。

在这里插入图片描述

D磁盘休眠状态(Disk sleep)

在操作系统中,如果一个进程处于睡眠状态,即S状态。如果此时操作系统负载过大,则可能杀死该进程。这也就是为什么某些软件服务在用户量过大时,出现某些用户无法获取服务的原因。

从操作系统角度来说,操作系统为了维护服务器能稳定运行,不得不杀死某些进程;从进程角度来说,进程正常运行而被操作系统强制关闭,进程也无能为力。

如果我们希望某些关键性进程,即使在操作系统负载过大时也不会被杀死,则可以将该进程设置为D状态,即磁盘休眠状态(也称为磁盘睡眠状态、深度睡眠状态)。处于该状态的进程不能被操作系统中断,也不能被操作系统唤醒。该进程只有自己醒来,才能被操作系统调度执行;只有该进程执行完毕,才能被操作系统回收。

★ps:该状态无法使用程序演示

T停止状态(stopped)

在操作系统中,可以给某些进程发送信号,发送信号的格式为:

kill -[信号编号] [进程pid]

下面,我们使用kill -l查看所有信号及其对应的编号↓↓↓
在这里插入图片描述
上图的18号信号SIGCONT为进程继续执行信号,19号信号为SIGSTOP为进程暂停信号。如果我们给某个进程发送19号信号,则它将处于T状态,即暂停状态。

下面,我们给上面循环打印"hello world"的进程发送19号信号,再对比发送信号前后的状态变化

kill -19 19093

在这里插入图片描述
在这里插入图片描述
在接收到19号SIGSTOP信号后,19093号进程停止打印"hello world",并且此时它的状态为T状态,即暂停状态。

如果我们给19093号进程发送18号SIGCONT信号,会是什么效果呢?
在这里插入图片描述
发送信号后,19093号进程继续打印"hello world",并且它的状态又变回S状态,即睡眠状态。但这里不同的是,原先的状态是S+,而此时的状态是S。这两者有什么区别呢?

S+状态下的进程,在使用ctrl+C时,可以被终止;而S状态下的进程使用ctrl+C却无法被终止。这里带有+号的称为前台进程,不带+号的称为后台进程。后台进程需要使用kill -9 [进程号]发送9号信号来终止。
在这里插入图片描述

★ps:T状态与S状态的区别:T状态单纯暂停,并不等待某种资源;而S状态是为了等待某种资源。

t调试/追踪状态(tracing stop)

我们在编写完程序后,可以在使用gcc编译,如果在编译命令的末尾加上-g选项,则会生成一个debug版本的程序。如果不带-g选项,gcc默认生成的是release版本。

我们使用gcc生成下面程序的release和debug版本↓↓↓

#include <stdio.h>

int Add(int left, int right)
{
	return left + right;
}

int main()
{
	int num1 = 10;
	int num2 = 20;
	printf("%d + %d = %d\n", num1. num2, Add(num1, num2));
	return 0;
}

在这里插入图片描述
从上图可以发现,debug版本所占的内存空间会大于release版本(因为debug版本中包含调试信息)。

下面我们使用gdb对test_g进行调试↓↓↓
在这里插入图片描述
由于在12行处打了断点,此时程序停止在12行处。我们使用ps axj | head -1 && ps axj | grep test查看当前进程状态↓↓↓
在这里插入图片描述
此时的进程状态为t状态,即调试状态(也称为追踪状态)。

X死亡状态/终止状态(dead)

如果进程执行结束了,操作系统会马上回收该进程的资源吗(进程此时占用内存等资源)?不一定。如果此时cpu上此时正在处理更加重要、紧急的进程,则操作系统此时不会马上回收已经执行结束的进程,而是将该进程标识为X状态,即死亡状态(也成为终止状态)。

标记为X状态的进程,表示该进程可以被操作系统回收。但具体什么时候回收,取决于操作系统。由于X状态瞬时性较强,难以使用程序演示,这里就不使用程序演示了。

Z僵尸状态/僵尸进程(zombie)

僵尸状态(zombie,也称为僵死状态)是一种比较特殊的状态。该状态发生在子进程退出,而父进程没有回收子进程资源时(即父进程没有使用waitpid读取子进程的退出信息),此时子进程就会进入僵尸状态。

僵尸进程会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,则子进程会进入Z状态。

下面创建的程序,使得子进程保持5秒的僵尸状态↓↓↓

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

int main()
{
	pid_t id = fork();
	assert(id != -1);

	if(id == 0)
	{
		printf("I am child process! pid = %d, ppid = %d\n", getpid(), getppid());
		sleep(5);
		exit(0);
	}
	printf("I am parent process! pid = %d\n", getpid());
	sleep(10);
	return 0;
}

执行上述程序并使用while :; do ps axj | head -1 && ps axj | grep test; sleep 1; echo "#############" done; 脚本,每1秒钟对执行内容做监视。
在这里插入图片描述
在这里插入图片描述
我们可以发现,在子进程退出后,父进程没有退出,此时的子进程的状态变为Z状态,即僵尸状态。

僵尸进程的危害:
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态。

维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护。

那一个父进程创建了很多子进程,如果不回收,就会造成内存资源的浪费。因为数据结构对象本身就要占用内存,想想C语言中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!

★ps:关于僵尸进程如何被处理的问题,将在后序文章中的介绍。

孤儿进程

上面已经将进程的各种状态讲述完毕,接下来,我们再了解另一种进程——孤儿进程。

父进程如果提前退出,子进程后退出,子进程进入Z之后,那该如何处理呢?

如果让子进程一直保持Z状态,则会造成内存泄漏;但此时子进程的父进程已经执行结束,没有进程可以来清理子进程的资源了,这种子进程被称为孤儿进程。操作系统为了解决这个问题,对于父进程已经执行结束,而子进程后退出的,该子进程将被1号init进程领养,其资源将由init进程进行回收。

下面代码中,父进程比子进程执行结束前5秒就退出↓↓↓

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

int main()
{
	pid_t id = fork();
	assert(id != -1);
	if(id == 0)
	{
		int cnt = 10;
		while(cnt > 0)
		{
			printf("I am child process, pid = %d, ppid = %d\n", getpid(), getppid());
			sleep(1);
			cnt--;
		}
		exit(0);
	}
	printf("I am parent process, pid = %d\n", getpid());
	sleep(5);
	return 0;
}

在这里插入图片描述
由程序执行结果可以看出,子进程前5秒的父进程pid为15480,由于父进程在子进程推出前5秒就推出了,此时子进程被1号init进程领养,故此时该进程的父进程pid为1。

进程优先级

基本概念

优先权高的进程有优先执行权利,可以优先获得cpu资源。

配置进程优先权对多任务环境的linux很有用,可以改善系统性能。对于多核计算机,可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

查看系统进程

我们可以执行ps -le来查看系统中所有进程的详细信息(-e选项表示所有进程,-l选项表示显示进程详细信息)
在这里插入图片描述
上图中:
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值

PRI及NI

PRI是进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高。NI就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值。PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。

这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行。所以,调整进程优先级,在Linux下,就是调整进程nice值。nice其取值范围是-20至19,一共40个级别。

那如果PRI(new)=PRI(old)+nice,那么我的程序起始优先级PRI为80,我对它重复设置10000次nice值为-20,它的优先级PRI是不是变成80-20*10000呢?答案是否定的。在Linux操作系统中,PRI起始都为80,nice值得范围为-19到20。当我们设置了新的nice值时,该进程的PRI=80+nice值。也就是说PRI的范围在[80-20,80+19]之间。

★ps:进程的nice值不是进程的优先级,进程优先级与nice值不是一个概念,但是进程nice值会影响到进程的优先级变化。可以理解nice值是进程优先级的修正修正数据。

修改进程优先级

top命令

首先,我们运行一个名为test的死循环程序。此时它的PRI为80,NI为0。
在这里插入图片描述
top命令修改进程优先级的方法(如果要设置小于0的nice值,即提高进程优先级,此时需要使用sudo提权):

①执行top命令
在这里插入图片描述
②输入r,并输入待修改进程优先级的进程pid。
在这里插入图片描述
③输入nice值,这里输入10。
在这里插入图片描述
我们使用ps -el | head -1 && ps -el | grep test查看进程优先级发现,test的PRI变为90,NI变为10。
在这里插入图片描述
★ps:上图中,我们将nice值设置为10,则该进程的PRI=80+10=90。如果我们在此基础上设置nice值为15,则该进程的优先级为PRI=80+15=95。

renice命令

renice命令使用格式为:

renice [nice值] -p [进程pid]

下图演示将20269号进程的nice值修改为15。
在这里插入图片描述
我们使用ps -el | head -1 && ps -el | grep test查看进程优先级发现,test的PRI变为95,NI变为15。
在这里插入图片描述
★ps:如果要给进程设置比原nice值更小的nice值,需要使用sudo提权。

关于进程的相关概念

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。

独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。

并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。

并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。

关于并行和并发这里做一下更详细的解释:
并行就是多个进程同时执行。而并发并不是,例如我们有3个进程在一个CPU上并发执行,第1个进程先执行1ms,接下来由第2个进程执行1ms,再由第3个进程执行1ms,然后又由第1个进程执行,以此类推…像这种明明各个进程是交替执行的,但由于各个进程都在向前运行,而进程交替运行的操作用户感知不到,用户以为这3个进程是各占用1个CPU并同时执行的,这种被称为并发。

🎈欢迎进入从浅学到熟知Linux专栏,查看更多文章。
如果上述内容有任何问题,欢迎在下方留言区指正b( ̄▽ ̄)d

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

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

相关文章

软考 系统架构设计师系列知识点之大数据设计理论与实践(2)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之大数据设计理论与实践&#xff08;1&#xff09; 所属章节&#xff1a; 第19章. 大数据架构设计理论与实践 第1节 传统数据处理系统存在的问题 最严重的问题是系统并没有对认为错误进行工程设计&#xff0c;仅靠备份是…

[C++]让C++的opencv库支持写出h264格式视频

当我们写下面测试代码时候&#xff1a; #include <opencv2/opencv.hpp>int main() {cv::VideoCapture cap("E:\\car.mp4"); // 打开默认摄像头if (!cap.isOpened()) {std::cout << "读取完毕!" << std::endl;return -1;}double fps ca…

HarmonyOS时区和语言设置-使用相关api实现系统语言和地区设置

介绍 本示例展示了i18n&#xff0c;intl&#xff0c;resourceManager在eTS中的使用&#xff0c;使用相关api实现系统语言和地区设置、时间和时区设置&#xff0c;展示了区域格式化示例。 效果预览 使用说明 1.启动应用&#xff0c;进入应用&#xff0c;首页分为三个按钮&…

Linux第87步_阻塞IO实验

阻塞IO是“应用程序”对“驱动设备”进行操作&#xff0c;若不能获取到设备资源&#xff0c;则阻塞IO应用程序的线程会被“挂起”&#xff0c;直到获取到设备资源为止。 “挂起”就是让线程进入休眠&#xff0c;将CPU的资源让出来。线程进入休眠后&#xff0c;当设备文件可以操…

nginx配置证书和私钥进行SSL通信验证

文章目录 一、背景1.1 秘钥和证书是两个东西吗&#xff1f;1.2 介绍下nginx配置文件中参数ssl_certificate和ssl_certificate_key1.3介绍下nginx支持的证书类型1.4 目前nginx支持哪种证书格式&#xff1f;1.5 nginx修改配置文件目前方式也会有所不同1.6 介绍下不通格式的证书哪…

Vue2.x实现商城购物车

1.实现购物车页面 在页面中显示购物车中的商品信息&#xff0c;并能进行数量增减及商品删除操作&#xff0c;购物车中金额也随商品数量的变化而变化 2.创建cart.html页面 创建cart.html页面&#xff0c;在其中创建Vue实例&#xff0c;实例中首先准备一些商品信息以供显示&a…

从TensorFlow到PyTorch:我为何选择后者开启深度学习之旅

在深度学习的浪潮中&#xff0c;TensorFlow和PyTorch无疑是两大最受欢迎的框架。它们各自拥有庞大的社区支持、丰富的文档资源和强大的功能&#xff0c;使得开发者们可以根据自己的需求选择合适的工具。然而&#xff0c;在我深入研究和实践之后&#xff0c;我最终选择了PyTorch…

Day20_学点儿JavaEE_基于Session的登录、数据库null值正确显示

1 登录 使用Session技术完成用户登录的功能&#xff1a; 登录功能会使用到Session&#xff0c;把用户登录的用户名和密码保存到Session&#xff0c;因为Session是属于每个用户独有的&#xff0c;就可以记录每个用户单独的登录信息。 当然&#xff0c;这仅仅是完成了一个简单的…

NL2SQL基础系列(1):业界顶尖排行榜、权威测评数据集及LLM大模型(Spider vs BIRD)全面对比优劣分析[Text2SQL、Text2DSL]

NL2SQL基础系列(1)&#xff1a;业界顶尖排行榜、权威测评数据集及LLM大模型&#xff08;Spider vs BIRD&#xff09;全面对比优劣分析[Text2SQL、Text2DSL] Text-to-SQL&#xff08;或者Text2SQL&#xff09;&#xff0c;顾名思义就是把文本转化为SQL语言&#xff0c;更学术一…

HTML和markdown

总体情况 <p>在html的用处 在vscode中使用markdown [Markdown] 使用vscode开始Markdown写作之旅 - 知乎

开源项目one-api的k8s容器化部署(上)-- 制作镜像及部署准备

一、背景 最近需要对开源项目one-api进行k8s容器化部署&#xff0c;主要分以下几个步骤&#xff1a; 制作docker镜像申请mysql和redis数据库docker-compose部署方式k8s部署方式 整个的篇幅比较长&#xff0c;将会分成上下两篇来阐述。 二、制作docker镜像 开源项目one-api…

计算机毕业设计选题之基于SSM的旅游管理系统【源码+PPT+文档+包运行成功+部署讲解】

&#x1f493;项目咨询获取源码联系v&#x1f493;xiaowan1860&#x1f493; &#x1f6a9;如何选题&#xff1f;&#x1f351; 对于项目设计中如何选题、让题目的难度在可控范围&#xff0c;以及如何在选题过程以及整个毕设过程中如何与老师沟通&#xff0c;有疑问不清晰的可…

迭代器模式【行为模式C++】

1.简介 迭代器模式是一种行为设计模式&#xff0c; 让你能在不暴露集合&#xff08;聚合对象&#xff09;底层表现形式 &#xff08;列表、 栈和树等&#xff09; 的情况下遍历集合&#xff08;聚合对象&#xff09;中所有的元素。 迭代器的意义就是将这个行为抽离封装起来&a…

非关系型数据库(缓存数据库)redis的集群

目录 一.群集模式——Cluster 1.原理 2.作用 3.特点 4.工作机制 哈希槽 哈希槽的分配 哈希槽可按照集群主机数平均分配&#xff08;默认分配&#xff09; 根据主机的性能以及功能自定义分配 redis集群的分片 分片 如何找到给定key的分片 优势 二. 搭建Redis群集…

Java快速入门系列-7(测试与调试)

第七章:测试与调试 第7章:测试与调试7.1 单元测试(JUnit)7.1.1 为什么要进行单元测试7.1.2 JUnit基础7.1.3 断言7.1.4 测试套件7.2 集成测试与系统测试7.2.1 集成测试7.2.2 系统测试7.3 调试技巧与工具7.3.1 断点7.3.2 单步执行7.3.3 变量检查7.3.4 条件断点7.3.5 日志记录…

Java快速入门系列-6(数据库编程与JDBC)

第六章:数据库编程与JDBC 6.1 SQL基础6.1.1 SQL基本结构与命令6.1.2 SQL高级查询6.1.3 SQL子查询与联接6.2 JDBC原理与使用6.2.1 JDBC驱动程序与URL6.2.2 Statement、PreparedStatement与CallableStatement6.2.3 数据库事务处理6.3 数据库连接池6.4 事务管理6.1 SQL基础 SQL(…

【Linux的进程篇章 - 环境变量的理解】

Linux学习笔记---007 Linux之进程优先级、环境变量以及地址空间的理解1、进程优先级1.1、什么是优先级&#xff1f;1.2、为什么要有优先级&#xff1f;1.3、Linux的优先级特点以及查看方式1.4、进程的几个特性 2、环境变量2.1、概念2.2、命令行参数2.2.1、什么是命令行参数&…

OVITO-2.9版本

关注 M r . m a t e r i a l , \color{Violet} \rm Mr.material\ , Mr.material , 更 \color{red}{更} 更 多 \color{blue}{多} 多 精 \color{orange}{精} 精 彩 \color{green}{彩} 彩&#xff01; 主要专栏内容包括&#xff1a; †《LAMMPS小技巧》&#xff1a; ‾ \textbf…

BTI下的JOP问题

BTI分支目标识别精讲与实践系列 思考 1、什么是代码重用攻击?什么是ROP攻击?区别与联系? 2、什么是JOP攻击?间接分支跳转指令? 3、JOP攻击的缓解技术?控制流完整性保护? 4、BTI下的JOP如何缓解?什么是目标着陆台? 5、BTI的架构细节?硬件原理?间接分支类型?指…

HUD抬头显示器中如何设计LCD的阳光倒灌实验

关键词&#xff1a;阳光倒灌实验、HUD光照温升测试、LCD光照温升测试、太阳光模拟器 HUD&#xff08;Head-Up Display&#xff0c;即抬头显示器&#xff09;是一种将信息直接投影到驾驶员视线中的技术&#xff0c;通常用于飞机、汽车等驾驶舱内。HUD系统中的LCD&#xff08;Liq…