【Linux进程(四)】深入理解 Linux O(1) 调度器:双队列轮转与进程优先级机制——如何避免进程饥饿,实现公平且高效的进程调度


🎬 个人主页艾莉丝努力练剑
专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:


文章目录

  • [3 ~> 进程](#3 ~> 进程)
    • [3.6 进程调度问题](#3.6 进程调度问题)
      • [3.6.1 Linux2.6内核进程O(1)调度队列](#3.6.1 Linux2.6内核进程O(1)调度队列)
      • [3.6.2 一个CPU拥有一个runqueue(rq)](#3.6.2 一个CPU拥有一个runqueue(rq))
      • [3.6.3 优先级](#3.6.3 优先级)
      • [3.6.4 活跃队列](#3.6.4 活跃队列)
      • [3.6.5 一个队列没办法做到公平调度------进程饥饿问题](#3.6.5 一个队列没办法做到公平调度——进程饥饿问题)
      • [3.6.6 过期队列(expired queue)](#3.6.6 过期队列(expired queue))
      • [3.6.7 实现双"140"队列轮转------active指针和expired指针](#3.6.7 实现双“140”队列轮转——active指针和expired指针)
      • [3.6.7 进程调度小结](#3.6.7 进程调度小结)
        • [3.6.7.1 两个重要结论](#3.6.7.1 两个重要结论)
        • [3.6.7.2 真正还在用的调度算法------进程调度O(1)算法](#3.6.7.2 真正还在用的调度算法——进程调度O(1)算法)
        • [3.6.7.3 Linux内核代码展示](#3.6.7.3 Linux内核代码展示)
        • [3.6.7.4 nice的名字由来](#3.6.7.4 nice的名字由来)
      • [3.6.8 思维导图](#3.6.8 思维导图)
      • [3.6.9 分时操作系统和实时操作系统的说明](#3.6.9 分时操作系统和实时操作系统的说明)
        • [3.6.9.1 例子:智能汽车(以特斯拉汽车为例)](#3.6.9.1 例子:智能汽车(以特斯拉汽车为例))
        • [3.6.9.2 原理](#3.6.9.2 原理)
      • [3.6.10 为什么是O(1)时间复杂度?nr_active](#3.6.10 为什么是O(1)时间复杂度?nr_active)
  • 结尾


3 ~> 进程

3.6 进程调度问题

艾莉丝会介绍一个真正还在用的调度算法------O(1)算法

3.6.1 Linux2.6内核进程O(1)调度队列

上图就是Linux2.6内核中进程队列的数据结构,它们之间的关系也已经以图示的形式给uu们呈现出来啦,方便理解。

3.6.2 一个CPU拥有一个runqueue(rq)

如果有多个CPU就要考虑进程个数的负载均衡问题

rq是个调度队列,包含很多属性。

3.6.3 优先级

  • 实时优先级:0~99(不关心,属于实时操作系统);
  • 普通优先级:100~139(都是普通的优先级,想想nice值的取值范围:40个,可与之对应)。

3.6.4 活跃队列

正在使用的队列叫做活跃队列

  • 时间片还没有结束的所有进程都按照优先级放在该队列。

  • nr_active:总共有多少个运行状态的进程。

  • queue[140]:一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进行排队调度,所以,数组下标就是优先级!

  • 从该结构中,选择一个最合适的进程,过程是怎么的呢?

1、从0下表开始遍历queue[140]。

2、找到第一个非空队列,该队列必定为优先级最高的队列。

3、拿到选中队列的第一个进程,开始运行,调度完成。

4、遍历queue[140]时间复杂度是常数!但还是太低效了。

  • bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32(160,其中20个没用,对应140个优先级和进程队列)个比特位表示队列是否为空,这样便可以大大提高查找效率!

3.6.5 一个队列没办法做到公平调度------进程饥饿问题

只有一个活跃队列(实时操作系统)是没办法做到公平调度的!

优先级更高的(60)进程始终不结束,而优先级低的(80)始终得不到CPU资源------进程饥饿问题

3.6.6 过期队列(expired queue)

活跃队列中的进程越来越少------都链入到过期队列去了------再去调度,进程都到过期队列当中去了。

时间片到了不能放到活跃队列,而是链入到过期队列中。

  • 过期队列和活动队列结构一模一样。
  • 过期队列上放置的进程,都是时间片耗尽的进程。
  • 当活动队列上的进程都被处理完毕之后,对过期队列的进程进行时间片重新计算。

3.6.7 实现双"140"队列轮转------active指针和expired指针

  • active指针永远指向活动队列;expired指针永远指向过期队列。

  • 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期时一直都存在的。

  • 没事哒,在合适的时候,只要能够交换(swap方法)active指针和expired指针的内容,就相当于有具有了一批新的活动进程!

即,轮转完毕,两个指针swap一下,交换一下指针------CPU视角:只要找到active队列。

3.6.7 进程调度小结

3.6.7.1 两个重要结论

双"140"队列来回轮转(swap两个指向两个队列的指针expired指针和active指针),就可以实现 "基本公平调度" 了。

  • 结论1:

调度没有直接用某个进程的具体优先级,优先级数字,决定的是进程Taskstruct入队列的问题!

  • 结论2:

在活跃队列的调度周期内,保证按照优先级调度。过期队列的存在,保证,优先级低的进程,可以被调度。

  1. 宏观上,所有进程都能被调度;

  2. 微观上,优先级高的先调度!

3.6.7.2 真正还在用的调度算法------进程调度O(1)算法

在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增加,我们称为进程调度O(1)算法

3.6.7.3 Linux内核代码展示
c 复制代码
struct rq {
 spinlock_t lock;
 /*
 * nr_running and cpu_load should be in the same cacheline because
 * remote CPUs use both these fields when doing load calculation.
 */
 unsigned long nr_running;
 unsigned long raw_weighted_load;
#ifdef CONFIG_SMP
 unsigned long cpu_load[3];
#endif
 unsigned long long nr_switches;
 /*
 * This is part of a global counter where only the total sum
 * over all CPUs matters. A task can increase this counter on
 * one CPU and if it got migrated afterwards it may decrease
 * it on another CPU. Always updated under the runqueue lock:
 */
 unsigned long nr_uninterruptible;
 unsigned long expired_timestamp;
 unsigned long long timestamp_last_tick;
 struct task_struct *curr, *idle;
 struct mm_struct *prev_mm;
 struct prio_array *active, *expired, arrays[2];
 int best_expired_prio;
 atomic_t nr_iowait;
#ifdef CONFIG_SMP
 struct sched_domain *sd;
 /* For active balancing */
 int active_balance;
 int push_cpu;
 struct task_struct *migration_thread;
 struct list_head migration_queue;
#endif
#ifdef CONFIG_SCHEDSTATS
 /* latency stats */
 struct sched_info rq_sched_info;
 /* sys_sched_yield() stats */
 unsigned long yld_exp_empty;
 unsigned long yld_act_empty;
 unsigned long yld_both_empty;
 unsigned long yld_cnt;
 /* schedule() stats */
 unsigned long sched_switch;
 unsigned long sched_cnt;
 unsigned long sched_goidle;
 /* try_to_wake_up() stats */
 unsigned long ttwu_cnt;
 unsigned long ttwu_local;
#endif
 struct lock_class_key rq_lock_key;
};
/*
 * These are the runqueue data structures:
 */
struct prio_array {
 unsigned int nr_active;
 DECLARE_BITMAP(bitmap, MAX_PRIO+1); /* include 1 bit for delimiter */
 struct list_head queue[MAX_PRIO];
};
3.6.7.4 nice的名字由来

这个设计太nice了,所以由此得名!

3.6.8 思维导图

一张图搞定:如下图所示------

Linux通过hash的方式维护40个链表------这不就是 开散列的哈希表 嘛!不同优先级的进程队列(里面都是相同优先级的进程)链接到不同的链表位置上, 按优先级数字大小,从低到高调度, 如此就实现了按优先级调度。

3.6.9 分时操作系统和实时操作系统的说明

3.6.9.1 例子:智能汽车(以特斯拉汽车为例)

我们用一个小小的示例来简单理解一下两种系统的区别。

  • 我们已经知道,"140"队列把前100个即[0 , 99]分给实时操作系统,后40个即[100 , 139]分给分时操作系统。

我们以智能汽车为例,前些年智能汽车兴起,特斯拉始终是行业翘楚,我们就以它为例吧!

  • 特斯拉的车载系统用的就是Linux操作系统。
3.6.9.2 原理

因为特斯拉的车载系统用的就是Linux操作系统,这里刹车的优先级高于听音乐,所以会先执行刹车,只有车停下来了,才可以执行听音乐或者其它的进程------刹车(优先级高的)优先于听音乐(优先级低的)。

3.6.10 为什么是O(1)时间复杂度?nr_active

unsigned int nr_active(--,一直到减为0,swap交换指针),Linux操作系统中的选择逻辑。

先拿active指针,找到active------找到现在是在哪一个队列当中,nr_active确定当前有无进程:如果有,查bitmap表,确认数组下标,根据数组下标,找到一个进程。

整个过程,时间复杂度几乎是O(1),这就是Linux内核之O(1)调度算法(结构决定算法)。


结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!

往期回顾

【Linux进程(三)】深入Linux进程调度:优先级(PRI, NI)的原理与实践操作全解

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
智驱力人工智能2 小时前
守护生命的水上之眼 无人机人员落水检测系统的技术攻坚与应用实践 无人机溺水识别 山区水库无人机落水检测系统 水域安全无人机部署指南
大数据·人工智能·算法·安全·无人机·边缘计算
xiatianxy2 小时前
有限空间监测设备,为石油储罐安全“保驾护航”
安全·有限空间作业·有限空间监测设备
好评1243 小时前
Linux入门:软件包管理、Vim、GCC、Makefile、Git 与 GDB
linux·运维·服务器
郑州光合科技余经理3 小时前
PHP构建:支撑欧美澳市场的同城生活服务平台开发
java·开发语言·数据库·uni-app·php·排序算法·生活
可爱又迷人的反派角色“yang”4 小时前
ansible剧本编写(三)
linux·网络·云计算·ansible
m0_738120724 小时前
应急响应——知攻善防Web-3靶机详细教程
服务器·前端·网络·安全·web安全·php
لا معنى له7 小时前
目标检测的内涵、发展和经典模型--学习笔记
人工智能·笔记·深度学习·学习·目标检测·机器学习
AKAMAI8 小时前
Akamai Cloud客户案例 | CloudMinister借助Akamai实现多云转型
人工智能·云计算
石像鬼₧魂石9 小时前
内网渗透靶场实操清单(基于 Vulhub+Metasploitable 2)
linux·windows·学习·ubuntu