【Linux系统】解明进程优先级与切换调度O(1)算法

【Linux系统】解明进程优先级与切换调度O(1)算法

各位读者大佬好,我是落羽!一个坚持不断学习进步的学生。
如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步!
也欢迎关注我的blog主页:
落羽的落羽

文章目录

一、进程优先级的概念

CPU的资源是有限的,所以CPU的运行队列中的所有进程是不可能同时得到资源的。这就是为什么运行队列是一个“队列”,而CPU分配资源的先后顺序,就是指进程的优先级。

二、查看优先级信息

使用ps -l命令,可以查看系统中更详细的进程信息:

在这里插入图片描述

其中,与进程优先级相关的信息是 PRI 与 NI ,它们也是存在于task_struct中的两个整型成员变量

  • PRI:代表这个进程可被执行的优先级,这个值越小越早被执行
  • NI:代表这个进程的nice值

1. PRI 与 NI 的理解

PRI比较好理解,就是进程的优先级,也就是程序被CPU执行的先后顺序,此值越小进程的优先级越高。
NI,是进程的nice值,表示进程优先级的修正数值

进程PRI值 = 80 + 进程nice值!

这是进程PRI的计算公式,nice变小,PRI会变小,进程优先级变高,反之亦然。
所以,调整进程优先级,在Linux下就是调整进程的nice值!
而优先级的变化范围是有限的!nice值的取值范围是-20~19,共40个级别。PRI的取值范围就是[60, 99]了!

2. 修改nice值

我们还是跑一个循环程序进行演示,此时查看PRI和NI是默认80和0,没有问题

## 二级目录

一种修改nice的方式是:用top命令,修改已存在进程的nice值:输入top回车,再输入r回车,再输入进程pid回车,再输入修改后的nice值

在这里插入图片描述

这里我将nice值修改为20。回车,按q退出top,再查询:

在这里插入图片描述

可以看到nice值成为了19,这是因为20超过了规定的nice值范围,最高只能修改到19。PRI值也就变成了19 + 80 = 99,没有问题。

但是,实际中我们不建议高频更改nice值。

三、进程调度切换

之前我们提到过,Linux系统会给每一个进程分配一个时间片,它的时间片执行完,就会自动让出CPU另一个进程接着执行。CPU内只有一套寄存器,由所有进程共享使用,每次保存着一个进程的私有上下文数据,它的时间结束后,这些数据转移到task_struct中的成员变量“上下文数据”中存放,寄存器中继续存放下一个进程的临时数据。再轮到这个进程执行时,临时数据再转移到寄存器中。这种操作系统,被称为分时操作系统,当代计算机大多数都是这种。

那么,系统是如何高效切换调度进程的呢?

下面讲解的,是Linux2.6内核进程的O(1)调度实现算法:

1. list_head 与 prio_array 结构

源码的task_struct定义中,有这样两个相关的成员变量:

在这里插入图片描述

这两个成员结构的定义如下:

成员prio_array_t *arrayprio_array_t就是struct prio_array结构体。task_struct 里的 array 成员是当前进程所属的优先级数组指针,记录了这个进程现在是在活跃队列里,还是在过期队列里,下面讲。

在这里插入图片描述


在这里插入图片描述

struct list_head是一个双向链表结构,run_list就是CPU运行队列的核心结构!

在这里插入图片描述

我们分析prio_array_t的结构:

在这里插入图片描述


所以,根据优先级选择进程队列的过程,本质是hash!

可是,queue中存放的队列的结点是list_head类型啊,list_head中只有两个前后指针,没有进程信息啊!

在这里插入图片描述

别急,一个知识点:

假设有一个结构体变量struct A obj,它内部有一个成员变量a。如果我不知道结构体obj的地址,只知道它的a的地址。可以通过&a - &((struct A*)0->a)计算得出obj的地址!原理是结构体成员的地址偏移量是固定的,而且地址是由低到高开辟的,通过成员地址减去其在结构体中的偏移量,即可反推出结构体变量的起始地址。得到了结构体变量的地址,就能访问到其他成员了!

所以,实际上queue中的每一个list_head,就是一个进程的task_struct的成员变量struct list_head run_list!通过上述方法,就能用这个run_list的地址算出task_struct的地址了,进而能获取到进程的各种信息!
这种将链表节点嵌入到目标结构体内部,把run_list作为task_struct 的成员,是 Linux 内核中经典的 “侵入式链表” 设计,好处是无需额外内存开销、灵活复用、增加链式结构管理的扩展性。甚至,你可以将不同类型的结构连接起来,只要它们都含有run_list成员!

在这里插入图片描述

现在我们已经明确队列的结构了,那么如何从中选取一个合适的进程呢?
最简单的想法,就是按照优先级的规则,从下标0开始遍历queue,找到第一个非空的队列,依次调度其中的进程。由于queue大小固定140,这种算法的时间复杂度是O(1)。
但是,我们总是想要更好的效率。在prio_array中,还有一个bitmap数组,实际上这是一个位图!他用140个比特位表示140个进程队列是否为空,利用位运算,大大提高查找非空队列的效率!

2. 活跃140队列与过期140队列

一个CPU,拥有一个runqueue结构,即CPU的运行队列,这个结构的定义如下:

在这里插入图片描述
  • active:称为活跃指针,指向的prio_array_t结构称为活跃140队列
  • expired:称为过期指针,指向的prio_array_t结构称为过期140队列
  • arrays:这个数组中,存放活跃队列、过期队列

活跃队列中,按照优先级,放着时间片还没有结束的所有进程。变量nr_active记录有多少个运行状态的进程。
过期队列中,放着时间片耗尽的进程。当活跃队列上的一个进程执行完,就放到过期队列上,重新计算它的时间片。

不难想象,随着进程不断被执行,活跃队列上的进程会越来越少,过期队列上的进程会越来越多。当活跃队列上所有进程都被执行完,直接交换active与expired指针,刚才的过期队列变成新的活跃队列,刚才的活跃队列变成新的过期队列!继续执行进程。

利用两套优先级队列调度切换进程,是非常巧妙的设计,我们会有以下特殊情况:

  • 来了一个新进程,则这个进程先插入到过期队列中,等下一轮再开始执行!
  • 修改一个进程的优先级,则这个进程在本轮中的位置先不变,等插入到过期队列时再计算新优先级位置,这也是为什么要有nice这个中间值的原因之一!

如果只搞一套队列,进程时间片用完或修改优先级时,需要在同一个队列里重新计算时间片、调整位置,会导致遍历队列的开销变大,新进程和过期进程混在一起,调度逻辑混乱。

有了这样一套完整的调度逻辑后,在系统中查找一个最合适的调度进程的时间复杂度就是一个常数,不随着进程增多而导致时间成本增加,我们称之为进程调度O(1)算法!

四、补充概念:竞争、独立、并行、并发

  • 竞争:系统进程众多,而CPU资源较少,所以进程之间是有竞争性的。为了高效完成任务,竞争合理,就有了进程优先级。
  • 独立:多进程运行,需要独享各种资源,各个进程运行之间互不干扰。父子进程之间也有独立性。
  • 并行:多个进程在多个CPU下分别同时运行,这种称之为并行。不过我们目前大部分的计算机都是只有一个CPU的。
  • 并发:多个进程在一个CPU下采用进程切换的方式,在一段时间内让多个进程都得以推进,称之为并发!

Read more

用 DeepSeek 打造你的超强代码助手

用 DeepSeek 打造你的超强代码助手

DeepSeek Engineer 是啥? 简单来说,DeepSeek Engineer 是一个基于命令行的智能助手。它能帮你完成这些事: * 快速读文件内容:比如你有个配置文件,直接用命令把它加载进助手,后续所有操作都可以基于这个文件。 * 自动改文件:它不仅能提建议,还可以直接生成差异表(diff),甚至自动应用修改。 * 智能代码生成:比如你让它生成代码片段,它会按照指定格式和规则直接返回。 更重要的是,这一切都是通过 DeepSeek 的强大 API 来实现的。想象一下,你有个贴身助手,不仅能听懂你的代码需求,还能直接动手帮你写! 核心功能拆解 我们先来看 DeepSeek Engineer 的几个核心能力,让你更好地理解它的强大之处。 1. 自动配置 DeepSeek 客户端 启动这个工具时,你只需要准备一个 .env 文件,里面写上你的 API Key,比如: DEEPSEEK_API_

By Ne0inhk
解锁DeepSeek潜能:Docker+Ollama打造本地大模型部署新范式

解锁DeepSeek潜能:Docker+Ollama打造本地大模型部署新范式

🐇明明跟你说过:个人主页 🏅个人专栏:《深度探秘:AI界的007》 🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、什么是Docker 2、什么是Ollama 二、准备工作 1、操作系统 2、镜像准备 三、安装 1、安装Docker 2、启动Ollama 3、拉取Deepseek大模型 4、启动Deepseek  一、引言 1、什么是Docker Docker:就像一个“打包好的App” 想象一下,你写了一个很棒的程序,在自己的电脑上运行得很好。但当你把它发给别人,可能会遇到各种问题: * “这个软件需要 Python 3.8,但我只有 Python 3.6!

By Ne0inhk
深挖 DeepSeek 隐藏玩法·智能炼金术2.0版本

深挖 DeepSeek 隐藏玩法·智能炼金术2.0版本

前引:屏幕前的你还在AI智能搜索框这样搜索吗?“这道题怎么写”“苹果为什么红”“怎么不被发现翘课” ,。看到此篇文章的小伙伴们!请准备好你的思维魔杖,开启【霍格沃茨模式】,看我如何更新秘密的【知识炼金术】,我们一起来解锁更加刺激的剧情!友情提醒:《《《前方高能》》》 目录 在哪使用DeepSeek 如何对提需求  隐藏玩法总结 几个高阶提示词 职场打工人 自媒体创作 电商实战 程序员开挂 非适用场地 “服务器繁忙”如何解决 (1)硅基流动平台 (2)Chatbox + API集成方案 (3)各大云平台 搭建个人知识库 前置准备 下载安装AnythingLLM 选择DeepSeek作为AI提供商 创作工作区 导入文档 编辑  编辑 小编寄语 ——————————————————————————————————————————— 在哪使用DeepSeek 我们解锁剧情前,肯定要知道在哪用DeepSeek!咯,为了照顾一些萌新朋友,它的下载方式我放在下面了,拿走不谢!  (1)

By Ne0inhk
【AI大模型】DeepSeek + 通义万相高效制作AI视频实战详解

【AI大模型】DeepSeek + 通义万相高效制作AI视频实战详解

目录 一、前言 二、AI视频概述 2.1 什么是AI视频 2.2 AI视频核心特点 2.3 AI视频应用场景 三、通义万相介绍 3.1 通义万相概述 3.1.1 什么是通义万相 3.2 通义万相核心特点 3.3 通义万相技术特点 3.4 通义万相应用场景 四、DeepSeek + 通义万相制作AI视频流程 4.1 DeepSeek + 通义万相制作视频优势 4.1.1 DeepSeek 优势 4.1.2 通义万相视频生成优势 4.2

By Ne0inhk