Linux进程队列的原理

进程优先级

1.进程优先级是什么

对比优先级<->权限

  • 权限:是能不能的问题;
  • 优先级:已经能了,先后的问题。

优先级:进程在已经能得到某种资源的情况下,得到资源的先后顺序。

2.为什么要有

资源不足,需要分配资源,就要设置优先级:决定进程,获得资源的先后顺序。

3.Linux下,怎么设计的?

查看系统进程 :命令ps -l

  • UID:对应此进程对应的用户编号,不同用户都有各自的编号;
  • PRI :代表此进程可被执行的优先级,值越小越早被执行;
  • NI :一个进程的nice值,优先级的修正数值。当nice值为负数,进程优先级将会变高,越快被进行。

所以,在Linux下,调整进程优先级就是调整进程的nice值,nice的范围是-20~19,一共40个级别。

通过top命令,按r进入修改指定进程的nice值;将某进程nice值改变后观察:

优先级不能频繁更改

注意 :通过nice值改动的PRI始终是PRI默认值(80)-nice值。比如当一开始对一个进程的nice值赋10,PRI变为90;第二次改nice赋值为-10,PRI变为70,而不是变回80。

4.nice范围

在修改某进程nice值的过程中,无论赋任何值,最终只能是在-20~19这个范围内;那么进程PRI范围只能是60~99

5.为什么优先级变化范围有限?

常见的操作系统Linux、windows等,都是分时操作系统

分时操作系统:给进程分配时间片,相对公平、公正的调度策略,较为均衡地让不同的进程,都能得到CPU的资源。

所以:改变优先级就不能改变得太狠,就要控制范围。

补充:实时操作系统

与分时操作系统相对。

只有执行完一个进程后,才会去执行下一个进程,在工业运用中比较多。

6.其他修改优先级的命令

1)nice

nice -n <调整值> <命令/程序>

  • <调整值>:是在默认值 0 的基础上加减,比如 -n 5 就是把 nice 值设为 0+5=5;
  • 普通用户:只能把 nice 值调大(降低优先级),范围 0~19;
  • root 用户:可以调小(提高优先级),范围 -20~19.

注意:当运行完nice这个命令时,会自动启动对应的进程/程序。

复制代码
# 示例1:降低优先级启动程序(普通用户也能做)
nice -n 10 ./main

# 示例2:提高优先级启动程序(需要root权限)
sudo nice -n -5 ./cpu_intensive_program

# 示例3:查看当前nice默认值
nice

2)renice

renice <调整值> -p <PID>

  • <调整值>:指定最终的 nice 值;
  • -p <PID>:目标进程的 PID;
  • 权限规则和 nice 一样:普通用户只能调大(降优先级),root 可以任意调。

注意:修改的是已经在运行的进程的nice值。

复制代码
# 示例1:降低某个进程的优先级(普通用户)
renice 15 -p 12345

# 示例2:提高某个进程的优先级(需要root)
sudo renice -5 -p 12345

7.补充概念

1)竞争性:CPU资源必然有限,进程必然众多,所以进程之间必然有竞争;

2)独立性 :多进程运行,需要独享各自的资源,进程间互不干扰;父子进程也有独立性!

3)并行 :多个进程在多个CPU下分别,同时进行运行,这称之为并行;(少见)

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

之为并发。

进程切换

进程上下文

1)作用进程切换 :保存旧进程上下文 → 加载新进程上下文,保证进程暂停后再恢复,能接着原来位置继续跑,不乱序、不丢数据。

2)包含三部分(了解):

  1. 用户级上下文:程序代码、数据、栈、全局变量、用户寄存器
  2. 寄存器上下文 (最核心):程序计数器 PC、栈指针、通用寄存器、状态字
  3. 系统级上下文:进程 PCB、内核栈、页表、内存映射、打开文件等内核信息

切换

因为并发 特点,当进程进行切换,因寄存器是共享的,A进程运行时,将A的上下文存进寄存器中,当需要中途离开系统进行下一个B进程时,A进程将寄存器里的上下文剥离存到A自己的task_struct中,然后B进程的上下文再存进寄存器中,运行B进程。

(时间片):当代计算机都是分时操作系统,每个进程都有它合适的时间片(其实就是⼀个计数

器)。时间片到达 ,进程就被操作系统从CPU中剥离下来。

组织进程

Linux是由c语言组织的,当创建一个结构体如下,它的地址怎么存的呢?

c 复制代码
struct A {
	int a;
    int b;
    int c;
    double d;
};

提出问题 :引用上面的结构体A,假如我知道一个对象的c成员 的地址,但是我不知道这个对象的地址,我要怎样求得这个对象的地址呢?

由此在进程之间可以重新设计一个双链表

1)如何获取进程的其他属性?

刚刚展示的,由成员变量地址可以求出整体对象的地址。所以这里我们知道了成员变量链表节点的地址,自然能求得整个进程的地址,也就能访问该进程的其他属性;

2)为何如此设计?

增加链式管理的扩展性,其他地方也能用这种结构;代码只需要维护一份;

3)节点连接

当新的进程需要连接上来时,只需要insert这个link node。

4)进程都在哪?

Linux内核,会将所有进程的task_struct统一放在一张全局的双链表list_head task中;

5)进程不是也会在运行队列、阻塞队列吗?

当然,说明task_struct中,不仅有一套list_head task,还会有其他的链表,如:struct list_head run代表的就是运行队列的链表。这也就使得进程能在不同的队列链表中自由切换。

甚至,不同的结构体对象也能连接起来;

甚至task_struct里还能接不同的数据结构,红黑树、hash表等。

Linux的调度(运行)队列

每一个CPU都有一个调度(运行)队列:struct runqueue{};

当系统要运行进程时,会在140个数组元素中找下标较小且非空的,然后在找到的元素中再看其中链表最靠前的,那一个就是要运行的进程。

那此时是需要去循环找到哪个元素吗?

不是的,Linux中,为了提高效率,采用了位图的方法检测:

以上就设计出一个结构:优先级数组

c 复制代码
struct prio_array_t {
	int nr_active;
	long bitmap[5];
	struct list_head queue[140];
}

问题

1)假如系统中有一些进程优先级是61,然而不断的有优先级是60的进程加进来,那么61的进程就一直不运行了?

当然不是,因为这是个分时操作系统,会有时间片的限制。

2)那是将60的进程PRI变大吗?

也不是,因为将PRI变大,将优先级变底,不仅变得是PRI的数字,还要将进程的所在队列更改,成本大。

解决办法

CPU的调度(运行)队列struct runqueue{};中会存在两个相同的优先级数组struct prio_array_t[2],一个是活跃队列 ,一个是过期队列 ,并且会有两个指针*active、*expired,各自指向一个队列。

任何运行的进程都是从活跃队列里拉出来的,当这个进程的时间片结束 了,这个进程会被放进过期队列中 的对应位置;当活跃队列为空时,两个指针指向将会调换,过期队列变成活跃队列,至此又来一轮进程的运行。

那么像问题(1)的现象,新来的60进程,不会直接接到活跃队列后面,而是接到过期队列后面,在下一轮过期-活跃对换时再运行60进程。

以上就是Linux进程队列的O(1)算法。

相关推荐
HLC++10 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室12 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy14 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕15 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫16 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生16 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1118 小时前
linux基础IO
linux·运维·服务器
BelongPanda19 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯20 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983420 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器