【Linux】深入理解Linux进程状态与优先级管理


文章目录

  • 前言
  • 一、操作系统学科下的进程状态
    • [1.1 运行状态](#1.1 运行状态)
    • [1.2 阻塞态](#1.2 阻塞态)
    • [1.3 挂起态](#1.3 挂起态)
  • 二、Linux系统下的进程状态
    • [1.1 运行态观察](#1.1 运行态观察)
    • [1.2 S阻塞、浅度睡眠状态](#1.2 S阻塞、浅度睡眠状态)
    • [1.3 D深度睡眠状态](#1.3 D深度睡眠状态)
    • [1.4 S停止状态](#1.4 S停止状态)
    • [1.5 X死亡状态](#1.5 X死亡状态)
    • [1.6 Z(zombie)-僵尸进程(重要)](#1.6 Z(zombie)-僵尸进程(重要))
  • 三、进程优先级
    • [3.1 概念](#3.1 概念)
    • [3.2 Linux如何设计优先级调度呢?](#3.2 Linux如何设计优先级调度呢?)
    • [3.3 操作系统如何根据优先级展开调度呢?](#3.3 操作系统如何根据优先级展开调度呢?)
    • [3.4 那么切换进程后再次回来的时候如何定位?](#3.4 那么切换进程后再次回来的时候如何定位?)
  • 总结

前言

在Linux系统下,系统通过PCB的方式管理进程(struct_task),在进程还未获得CPU资源时处于等待就绪状态,排队依次被CPU执行。在本文中我们将会了解到进程还有哪些状态,以及Linux系统下的实现方式是什么样的。


一、操作系统学科下的进程状态

1.1 运行状态

处于运行队列中的进程,进程获得CPU资源并正在执行指令的状态。在单核系统中,任何时刻只有一个进程处于运行态。

  • 时间片轮转机制:
    现代操作系统通过时间片(Timeslice)实现多任务并发。每个进程获得CPU的时间单位为毫秒级(通常5-100ms),当时间片耗尽时,进程会被强制放回运行队列尾部重新排队。
  • 并发与并行:
    在单核CPU中,通过快速切换时间片实现并发执行;多核CPU则能实现真正的并行执行。例如4核CPU可同时有4个进程处于运行态。
  • 运行队列管理:
    所有就绪态的进程PCB(Process ControlBlock)存储在运行队列(Runqueue)中,调度器根据优先级选择下一个执行的进程。

1.2 阻塞态

进程因等待外部事件(如I/O完成、资源就绪)而暂停执行的状态。此时进程不占用CPU,主动释放资源进入阻塞队列

在c语言学习阶段,scanf这种等待输入情况就是我们遇到的阻塞状态。

1.3 挂起态

挂起态是特殊的阻塞状态,当系统内存不足时,内核会将部分进程的映像交换到磁盘交换区(Swap)。该状态特点:

  • 进程的代码和数据被移出物理内存
  • 仅保留PCB在内存中
  • 需要换入(Swap in)才能恢复执行

二、Linux系统下的进程状态

操作系统的理论与实现并不一样,不同的操作系统有着不同的实现方式。

c 复制代码
/*
* 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 */
};

1.1 运行态观察

c 复制代码
// running.c
int main() {
    while(1);  // 无限循环
}

1.2 S阻塞、浅度睡眠状态

阻塞的本质就是在等待某种资源就绪,

典型场景:vim编辑scanf,scanf等待时,进程进入了S+阻塞状态,在等待键盘的输入准备,当输入完毕按下回车的一瞬间,进程变为R运行状态运行完毕。实际上,有很多进程都是阻塞状态,都需要等待诸如键盘、鼠标或磁盘等外设是否准备好资源。

1.3 D深度睡眠状态

典型场景:等待磁盘写入,进程不能被系统杀死。不响应任何请求

1.4 S停止状态

进程被信号暂停(如Ctrl+Z)

1.5 X死亡状态

这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

1.6 Z(zombie)-僵尸进程(重要)

父进程不回收子进程,子进程运行完了会进入僵尸进程状态,导致内存泄漏。在父子关系进程中,如果父进程先退出,父进程会被改成1号进程(被操作系统领养),子进程成为了孤儿进程,被系统领养。因为孤儿进程也要被回收,只能托孤给操作系统,等孤儿进程结束,会释放内存。

2案例2:僵尸进程生成

c 复制代码
 // zombie.c
#include <stdlib.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();
    if (pid == 0) {   // 子进程
        exit(0);      // 立即退出
    } else {          // 父进程
        sleep(60);    // 不回收子进程
    }
    return 0;
}

僵尸进程的危害:

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

维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的! 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空 间!

内存泄漏?是的!

三、进程优先级

3.1 概念

进程优先级概念:在对于资源的访问时,规定了谁先访问,谁后访问

文件权限:有没有权限访问

如果一个进程长时间得不到CPU资源,进程的代码长时间无法得到推进,导致进程的饥饿问题(windows下的未响应)。因此,操作系统需要保证进程之间的良性竞争需要确认进程的优先级。

3.2 Linux如何设计优先级调度呢?

  1. 查看优先级
bash 复制代码
ps -al | grep -1 && grep myproc

PRI优先级:最终优先级值,范围60-99(值越小优先级越高)

NI(nice值 ):进程优先级的修正数据。该数据意味着进程优先级是可以被修改的,实际上不能任意或过多的修改进程优先级 ,在Linux中,nice值是有范围的[-20~19]对应了四十个级别的优先级,所以优先级最高最低位(60,99)

计算公式: PRI(new) = PRI(old) + NI

  1. 调整优先级
bash 复制代码
$ nice -n -5 ./myprogram  # 启动时设置NI为-5
$ renice -10 -p 1234      # 修改PID为1234的进程 NI值为-10

3.3 操作系统如何根据优先级展开调度呢?

在Linux中,每个进程都有一个优先级,Linux使用一个包含40个不同级别的位图来管理进程的优先级。该位图称为"nice"值位图,它用于确定进程在被调度时的优先级。在Linux中,nice值的范围通常为-20到+19,其中-20表示最高优先级,+19表示最低优先级。

每个进程的nice值位图中的每一个位代表一个特定的优先级级别,如果某个位为1,则表示该进程对应的优先级级别可用,如果为0,则表示该优先级不可用。通过设置nice值,可以调整进程在系统中的优先级,以便进行合理的资源分配和任务调度。

位图:Linux内核O(1)调度算法。
其他概念

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

独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行

并发:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发;(进程切换 + 时间片限制 = 基于进程切换时间片轮转的调度算法)

但高优先级被轮换下次又将进入第一位执行,那这样是不是也耽误了其他进程的执行?(维护两批队列,执行队列和等待队列,高优先级进程被切换至等待队列,这样一来就避免了高优先级进程插队的现象)

3.4 那么切换进程后再次回来的时候如何定位?

CPU内部存在大量的寄存器,寄存器堆具有临时保存进程相关数据的能力,存储的是进程的上下文数据,进程在从CPU上离开的时候,要将自己的上下文数据保存 起来,以便再次执行的时候恢复到上一次的执行位置。在往后可能涉及中断等知识,这里不再叙述了。

当进程切换发生时,CPU需要完成以下操作:
1.保存当前进程的寄存器状态到PCB
2.加载新进程的寄存器状态
3.更新内存管理单元(MMU)的页表
4.刷新TLB缓存

为什么函数的返回值会被外部拿到呢?

通过寄存器拿到

系统如何得知我们的进程当前执行到哪一行呢?

程序计数器 (PC指针 / EIP):记录当前进程正在执行的指令的下一行


总结

通过本文的学习,读者应该能够全面理解Linux进程状态转换机制,并掌握基本的进程管理技巧。

👍 ​感谢各位大佬观看。如果本文有帮助,请点赞收藏支持~

相关推荐
余辉zmh3 分钟前
【Linux系统篇】:进程流水线的精密齿轮--创建,替换,终止与等待的工业级管控艺术
linux·运维
沙振宇5 分钟前
【操作系统】Docker如何使用-续
运维·docker·容器
2401_897930065 分钟前
docker 启动ElasticSearch
linux·前端·chrome
m0_7453642436 分钟前
Nginx反向代理及负载均衡
运维·nginx·负载均衡
yunbao00_1 小时前
网络基础(二)
运维·服务器·网络
风筝有风+1 小时前
学习应用层
服务器·网络·学习
榆榆欸1 小时前
1.基于TCP的简单套接字服务器实现
linux·网络·tcp/ip
Fanmeang1 小时前
ISIS-3 LSDB链路状态数据库同步
运维·网络·数据库·华为·智能路由器·ensp·isis
碳烤小肥杨..1 小时前
单臂路由实验
服务器·网络·网络协议·网络安全·智能路由器
珹洺1 小时前
计算机网络:(三)计算机网络体系结构(附带图谱表格更好对比理解)
运维·服务器·网络·数据库·网络协议·计算机网络·网络安全