Linux---进程(五)进程调度

一、

Linux内核中用双向链表管理进程控制块。

在OS的角度,Linux内核的struct list_head是一种侵入式的双向链表,把链表节点直接嵌入到task_struct 等数据结构中,有点非常突出:

(1)通用性极强:链表操作不依赖具体的数据类型,同一套API可以管理任意的结构体,避免了为每种数据结构重复写链表代码。

(2)多队列共存:一个task_struct可以嵌入多个list_head,比如tasks(全局进程链表)、run(运行队列)、children(子进程链表),让同一个进程同时属于多个队列,方便不同场景的管理。

(3)高效灵活:双向链表的插入删除操作是O(1)时间复杂度,内核平频繁调度进程时性能极高;同时通过container_of宏可以从链表节点快速反查到所属的task_struct

(4)内存开销小:链表节点只存指针,不存数据,避免了额外的内存开销,非常适合资源紧张的内核环境。

每一个CPU都有一个调度队列。

不同的结构体对象,也可以用链表链接;只要在不同的结构体里都嵌入struct list_head,就可以用同一套API把他们串起来。

二、调度队列

queue[140]

如上图:这个是调度队列

位图(bitmap)

1、位图(bitmap)到底是什么?

在 Linux O(1) 调度器的语境下,位图(bitmap) 就是一个由 0 和 1 组成的二进制数组,它的核心作用是:快速标记"哪些优先级队列里还有待运行的进程"。

它是 prio_array_t 结构体里的 bitmap[5] 成员。

它的每一位(bit),都对应着 queue[140] 数组里的一个优先级队列。

2、位图和优先级队列的关系

  1. 对应关系:

位图的第 i 位 ↔ 优先级为 i 的队列 queue[i] 。

  • 位图第 i 位为 1 → queue[i] 非空(有进程等待调度)。

位图第 i 位为 0 → queue[i] 为空(没有进程等待调度)。

  1. 结构关系:

你图中的 prio_array_t 结构体包含两个数组: array[0] (活跃进程)和 array[1] (过期进程)。

每个数组里都有自己独立的 bitmap[5] 和 queue[140] ,分别管理各自的进程队列状态。

3、如何判断一个队列是否为空?

判断一个队列是否为空,有两种方式:

  1. 直接检查队列:

去看 queue[i] 这个链表的头指针,如果它指向自己(即链表为空),则队列为空。

这种方法是 O(1),但如果要找最高优先级,就需要遍历所有队列,变成 O(n)。

  1. 通过位图间接判断(O(1) 调度器的核心):

当一个进程加入 queue[i] 时,内核会把位图的第 i 位 置为 1。 当 queue[i] 里最后一个进程被取走时,内核会把位图的第 i 位 清为 0。

所以,调度器在选择下一个进程时,不需要逐个检查队列,只需要扫描位图,找到第一个值为 1 的位,这个位的索引 i 就是当前最高优先级,对应的 queue[i] 就是非空的。

4、为什么要用位图?

速度快:把"遍历 140 个队列"的 O(n) 操作,变成了"扫描位图找第一个 1"的 O(1) 操作。

空间省:用 140 个比特(约 18 字节)就存下了所有队列的状态信息,非常紧凑。

设计巧:这正是 O(1) 调度器名字的由来,它能在常数时间内完成调度决策。

nr_active

记录总进程个数

进程调度规则

Linux O(1)调度算法

1、刚开始,所有进程都是在active里面的,expired是空的,调度器只从active里面选进程。

2、每个进程都会有时间片,时间一到,就把这个进程从active移到expired,重新给它分配时间片。

3、等到active里面一个进程都没有了,系统不复制,不搬运,只做一件事,把active和expired交换身份。此时就是原来的active变为新的expired,原来的expired变为active。(两个指针交换)

4、现在新的active里面全是重新充好时间片的进程,调度器又开始从这里面选择进程运行。

按照上面的规则一直循环往复

相关推荐
切糕师学AI1 分钟前
Kubernetes Operator 详解
运维·分布式·云原生·容器·kubernetes·自动化·运维自动化
Hello World . .2 分钟前
Linux:网络编程-基于HTTP协议的天气预报查询系统开发详解
linux·网络·http
软件资深者15 分钟前
macOS Tahoe 26.3.1 ISO 虚拟机专用镜像:win系统/ESXi 服务器装苹果系统,改个后缀就能用
运维·服务器·macos·镜像·虚拟机
艾莉丝努力练剑21 分钟前
【Linux进程间通信:共享内存】为什么共享内存的 key 值由用户设置
java·linux·运维·服务器·开发语言·数据库·mysql
贝锐25 分钟前
多窗口同时远控提效,向日葵助力企业应对批量运维难题
运维·远程控制
微露清风28 分钟前
系统性学习Linux-第四讲-进程控制
linux·服务器·学习
不脱发的程序猿28 分钟前
嵌入式Linux:阻塞式I/O与非阻塞式I/O
linux·服务器·单片机·嵌入式硬件·嵌入式
Qt程序员42 分钟前
基于 C++ 实现自定义字符串 string 类
linux·c++·容器·指针·内存管理·运算符重载
双层吉士憨包1 小时前
Google Voice保号教程
大数据·服务器·人工智能
fengyehongWorld1 小时前
docker 常用命令
运维·docker·容器