进程管理--CFS调度器(1)

news2024/11/23 20:33:23

介绍

CFS(Completely Fair Scheduler,完全公平调度器)用于Linux系统中普通进程的调度。它给cfs_rq(cfs的run queue)中的每一个进程设置一个虚拟时钟,vruntime。如果一个进程得以执行,随着时间的增长(一个个tick的到来),其vruntime将不断增大。没有得到执行的进程vruntime不变。调度器总是选择vruntime跑得最慢的那个进程来执行。这就是所谓的“完全公平”。为了区别不同优先级的进程,优先级高的进程vruntime增长得慢,以至于它可能得到更多的运行机会。

CFS不区分具体的cpu算力消耗型进程,还是io消耗型进程,统一采用红黑树算法来管理所有的调度实体sched_entity,算法效率为O(log(n))。每个进程都由一个struct task_struct来表示,为什么这里又定义了一个sched_entity?这是由于调度需要一些更详细的信息,比如当前运行时间或上次运行时间,因此就搞出了一个sched_entity的概念,为了存储一些调度相关的信息,给调度器用。CFS跟踪调度实体sched_entity的虚拟运行时间vruntime,平等对待运行队列中的调度实体sched_entity,将执行时间少的调度实体sched_entity排列到红黑树的最左边。调度实体sched_entity通过enqueue_entity()和dequeue_entity()来进行红黑树的出队入队。

相关结构体

总览

  • struct sched_entity:调度实体,这个也是CFS调度管理的对象
  • struct cfs_rq:CFS运行队列,该结构中包含了struct rb_root_cached红黑树,用于链接调度实体struct sched_entity。rq运行队列中对应了一个CFS运行队列,此外,在task_group结构中也会为每个CPU再维护一个CFS运行队列。
  • struct task_group:组调度,Linux支持将任务分组来对CPU资源进行分配管理,该结构中为系统中的每个CPU都分配了struct sched_entity调度实体和struct cfs_rq运行队列,其中struct sched_entity用于参与CFS的调度
//红黑树
struct rb_root_cached {
	struct rb_root rb_root; //红黑树的根节点
	struct rb_node *rb_leftmost; //红黑树中最左边节点
};

//cfs就绪队列 
struct cfs_rq {
	struct load_weight load;
	unsigned int nr_running; //就绪队列上调度实体的个数
	u64 min_vruntime; //就绪队列上所有调度实体中的最小虚拟时间

    /*用于跟踪调度实体按虚拟时间大小排序的红黑树的信息
    (包含红黑树的根以及红黑树中最左边节点)*/
	struct rb_root_cached tasks_timeline;
}; 
struct sched_entity {
	struct load_weight		load; //权重信息,在计算虚拟时间的时候会用到其中的inv_weight成员
    
    /*CFS调度器的每个就绪队列维护了一颗红黑树,上面挂满了就绪等待执行的task
      run_node就是挂载点*/
	struct rb_node		run_node; 

    //调度实体se加入就绪队列后,on_rq置1。从就绪队列删除后,on_rq置0
	unsigned int		on_rq;
    
    //调度实体已经运行实际时间总合
	u64			sum_exec_runtime;

    //调度实体已经运行的虚拟时间总合
	u64			vruntime;
}; 

调度类算法

struct sched_class {
	const struct sched_class *next;

	void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);
	void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);
    struct task_struct * (*pick_next_task)(struct rq *rq,
					       struct task_struct *prev,
					       struct rq_flags *rf);
	void (*yield_task)   (struct rq *rq);
	bool (*yield_to_task)(struct rq *rq, struct task_struct *p, bool preempt);

	void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);

  1. next:next成员指向下一个调度类(比自己低一个优先级)。在Linux中,每一个调度类都是有明确的优先级关系,高优先级调度类管理的进程会优先获得cpu使用权。
  2. enqueue_task:向该调度器管理的runqueue中添加一个进程。我们把这个操作称为入队。
  3. dequeue_task:向该调度器管理的runqueue中删除一个进程。我们把这个操作称为出队。
  4. check_preempt_curr:当一个进程被唤醒或者创建的时候,需要检查当前进程是否可以抢占当前cpu上正在运行的进程,如果可以抢占需要标记TIF_NEED_RESCHED flag。
  5. pick_next_task:通过next指针便利调度类链表,按照优先级顺序选择下一个最适合调度运行的任务,将其从rq移除,。

现在我们总结一下。Linux中所有的进程使用task_struct描述。task_struct包含很多进程相关的信息(例如,优先级、进程状态以及调度实体等)。但是,每一个调度类并不是直接管理task_struct,而是引入调度实体的概念。CFS调度器使用sched_entity跟踪调度信息。CFS调度器使用cfs_rq跟踪就绪队列信息以及管理就绪态调度实体,并维护一棵按照虚拟时间排序的红黑树。tasks_timeline->rb_root是红黑树的根,tasks_timeline->rb_leftmost指向红黑树中最左边的调度实体,即虚拟时间最小的调度实体(为了更快的选择最适合运行的调度实体,因此rb_leftmost相当于一个缓存)。每个就绪态的调度实体sched_entity包含插入红黑树中使用的节点rb_node,同时vruntime成员记录已经运行的虚拟时间。

基本概念

nice值(普通进程的优先级)

CFS调度器针对优先级提出了nice值的概念,其实和权重是一一对应的关系。nice值就是一个具体的数字,取值范围是[-20, 19]。数值越小代表优先级越大,同时也意味着权重值越大,nice值和权重之间可以互相转换。内核提供了一个表格转换nice值和权重。

const int sched_prio_to_weight[40] = {
 /* -20 */     88761,     71755,     56483,     46273,     36291,
 /* -15 */     29154,     23254,     18705,     14949,     11916,
 /* -10 */      9548,      7620,      6100,      4904,      3906,
 /*  -5 */      3121,      2501,      1991,      1586,      1277,
 /*   0 */      1024,       820,       655,       526,       423,
 /*   5 */       335,       272,       215,       172,       137,
 /*  10 */       110,        87,        70,        56,        45,
 /*  15 */        36,        29,        23,        18,        15,
}; 

数组的值可以看作是公式:weight = 1024 / 1.25nice计算得到。公式中的1.25取值依据是:进程每降低一个nice值,将多获得10% cpu的时间。公式中以1024权重为基准值计算得来,1024权重对应nice值为0,其权重被称为NICE_0_LOAD。默认情况下,大部分进程的权重基本都是NICE_0_LOAD。

virtual time(虚拟时间)

CFS调度器没有时间的概念,而是根据虚拟运行时间(抽象表示任务已运行时间)对任务进行排序,选择虚拟运行时间最小的进程进行调度

实际运行时间到 vruntime 的计算公式为:

[ vruntime = 实际运行时间 * 1024 / 进程权重 ]

vruntime值较小就说明它以前占用cpu的时间较短,受到了“不公平”对待,因此下一个运行进程就是它。这样既能公平选择进程,又能保证高优先级进程获得较多的运行时间,这就是CFS的主要思想。

执行时间

挑选的进程进行运行了,它运行多久?

进程运行的时间是根据进程的权重进行分配。

[ 分配给进程的运行时间 = 调度周期 *(进程权重 / 所有进程权重之和) ]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1045207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pycharm在进行debug时出现collecting data如何解决?

Pycharm在进行debug时变量界面出现collecting data,问题如下: 解决方法:打开Setting界面,在Python Debugger选项中勾选下图中的Gevent compatible即可。

iOS CocoaPod 打包:SDK开发、Pod组件生成等

参考链接:CocoaPod打包 SDK开发 - 简书 iOS非集成打包:依赖cocoapods的Swift静态库打包、脚本合并真机与模拟器 - 简书 iOS 组件化开发----pod私有库制作及使用_ios组件化开发-CSDN博客 1.生成pod包命令 pod lib create testTools 如果提示&#xf…

img 固定宽高 图像不拉伸 显示图片中间部分

.m-sd-chat-select-avatar-img{width: 100px;height: 125px;object-fit: cover;border-radius: 6px;cursor: pointer;} 使用后: 使用前:

Django 联表查询操作

在日常的开发中,常常需要对多张数据表同时进行数据查询。多表查询需要在数据表之间建立表关系才能够实现。一对多或一对一的表关系是通过外键实现关联的,而多表查询分为正向查询和反向查询。 表模型结构 以歌手表、专辑表、单曲表查询为例子。 歌手与专…

RK3588 VDD_LOGIC电源PCB设计注意事项

RK3588 VDD_LOGIC电源PCB设计 1、VDD_LOGIC的覆铜宽度需满足芯片的电流需求,连接到芯片电源管脚的覆铜足够宽,路径不能被过孔分割太严重,必须计算有效线宽,确认连接到CPU每个电源PIN脚路径都足够。 2、如图1所示,原理…

Scrapy-应对反爬虫机制

参考自https://blog.csdn.net/y472360651/article/details/130002898 记得把BanSpider改成自己的项目名,还有一个细节要改一下,把代码user换成user_agent 禁止Cookie 在Scrapy项目中的settings文件,可以发现文件中有以下代码: COOKIES_ENA…

红黑树-自平衡二叉搜索树

一、简介 红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,它的节点可以是红色或黑色。这个颜色的设计是为了满足红黑树的五个关键性质,确保树保持平衡和高效地支持插入、删除和搜索操作。 以下是红黑树的五个关键性质&#xf…

【Unity3D日常开发】Unity3D中Quality的设置参考

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 这篇文章就来讲一下Quality的设置(Unity版本&#…

浪潮信息被Gartner评为全球文件存储标杆厂商

近日,国际权威研究机构 Gartner 正式发布《2023年存储和数据保护技术成熟度曲线报告》((Hype Cycle for Storage and Data Protection Technologies, 2023,以下简称“报告”),基于对市场应用的前瞻洞察和多年的技术精耕…

518抽奖软件,一键打印中奖名单的方法和用途

518抽奖软件简介 518抽奖软件,518我要发,超好用的年会抽奖软件,简约设计风格。 包含文字号码抽奖、照片抽奖两种模式,支持姓名抽奖、号码抽奖、数字抽奖、照片抽奖。(www.518cj.net) 一键打印中奖名单 主窗口上按 CtrlP 打开 {…

在关系型数据库中储存树形结构

adjacency list 邻接表显然是最简单的方式,也是在实践中经常用到的。其储存节点以及直接父节点来进行储存树形结构 邻接表结构简单,查询修改节点的直接父节点都很容易。然而如果返回父节点下的所有节点之类的跨层操作那就很麻烦了,需要频繁…

matlab实现杨氏双缝干涉实验可视化界面

关于杨氏双缝干涉实验的条纹光强理论推导和matlab绘图可以参考下面的链接:杨氏双缝干涉实验matlab实现 接下来利用GUI实现可视化界面。 一、GUI GUIDE简介 1、在命令行窗口输入小写的guide可以自动弹出fig窗口。 2、界面的左侧是常用的工具,鼠标悬停…

利用串口示波器调试PID参数

PID调试最麻烦的是参数调整,需要花费大量时间,如果每调整一次都要修改代码重新编译烧录,效率很低,推荐串口示波器与rt-thead finsh进行调试 推荐使用FireWater数据引擎 定时上报数据,rt_kprintf不支持浮点数据打印&a…

从零开始之了解电机及其控制(6)六步换向法

引导:六步换向的本质是? 因为无刷电机有三根线,而H桥可以将负载连接到正电压或者地,于是用三对MOS管组成的H桥驱动电机,称为半桥驱动。 无刷电机的优点如下: 首先,由于所有三个电机相位都通过…

xcode15下载ios17模拟器失败

升级到xcode15后需要安装ios17模拟器 但是在下载过程中会遇到报错 如下图这种 网上搜索了一下发现有人遇到过无法下载的问题,并且在apple官网也有人提出类似问题 https://developer.apple.com/forums/thread/737648 解决方案就是从https://developer.apple.com/do…

linux提权秘籍

Linux 提权总结 一、常用基础 1、自定义可执行文件(Custom Executable) 可能有某些根进程执行另一个可以控制的进程。在这些情况下,以下C代码一旦编译,将生成一个作为根运行的sbashell: int main() { setuid(0);system("/bin/bash -p…

MapBox GL JS出现“Unimplemented type: 7”问题的解决办法

Mapbox GL JS在矢量瓦片的渲染方面有独特的优势,可以支持动态的样式,支持字体切片,快速加载各种字体。使用起来十分方便,但是在很长的一段时间内,经常遇到出现大量“Unimplemented type: 7”的控制台错误提示&#xff…

Web自动化测试 —— headless无头浏览器!

一、Options概述 是一个配置浏览器启动的选项类,用于自定义和配置Driver会话常见使用场景: 设置无头模式:不会显示调用浏览器,避免人为干扰的问题。设置调试模式:调试自动化测试代码(浏览器复用) 二、添加启动配置 添…

丰田 Auris 混动车电池冷却系统异常

故障现象 一辆丰田 Auris 混合动力车行驶时,混合动力车警告灯亮起。这辆车被改装成监控用车,车厢内到处都装有敏感的录音设备。 为了不被下面的情况所影响,我们从混合动力控制单元(HCU)中提取了故障代码 P0A82-123。混…

Multisim14.0仿真(二十四)基于LM555定时器的施密特触发器

一、仿真原理图: 二、仿真效果图: