Linux CFS 调度器 (1):概述

文章目录

  • 1. 前言
  • 2. CFS 调度器
    • 2.1 概述
    • 2.2 一些实现细节
    • 2.3 运行队列:红黑树
    • 2.4 一些特征
    • 2.5 调度策略
    • 2.6 调度器类别
    • 2.7 扩展:组调度
  • 3. 参考资料

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. CFS 调度器

2.1 概述

CFS,是 Completely Fair Scheduler 的缩写,翻译过来就是 完全公平调度器,由 Ingo Molnar 实现,在 Linux 2.6.23 中合入的新的 “桌面” 进程调度器。

CFS 调度器 80% 的设计可以用一句话来概括:CFS 基本上是在真实硬件上模拟了一个理想、精确多任务 虚拟 CPU。我们将这个虚拟 CPU 的运行能力定义为 1,假定当前有 nr_running 个任务在虚拟 CPU上运行,则每个任务精准的占用虚拟 CPU 1/nr_running运行能力(或 运行时间)。例如有 2 个任务在运行,假定将 虚拟 CPU 的运行能力设定为 100%,那么每个任务占用 虚拟 CPU 50%运行能力(或 运行时间)。为了描述方便,在这里先将任务占用的 虚拟 CPU 的运行时间,称作 虚拟运行时间(virtual runtime),这和后文的 物理 CPU物理运行时间 相对应。

CFS 调度器中,并非真的将 物理 CPU物理运行能力(或 物理时间)平均分配给所有可运行任务CFS 仍然要处理任务优先级:即优先级更高的任务,仍然会分配更多 物理 CPU物理运行时间 给它们。这看起来似乎和 CFS 调度器的 完全公平调度 宗旨相矛盾,但事实是,CFS 调度器只是尽量保证每个任务占用的 虚拟运行时间(virtual runtime) 一样,而任务占用的 物理运行时间,仍然由任务优先级来体现。正如世界不可能完全公平一样,在 CFS 调度器中,分配给每个任务的 虚拟运行时间(virtual runtime)CFS 也只是尽量保证它们一致不可能达到理想状态的完全一致

2.2 一些实现细节

CFS 调度器中,虚拟运行时间(virtual runtime) 通过每个任务的 p->se.vruntime(以纳秒单位)来表示和跟踪,这样,就可以准确地标记时间戳并测量任务应该获得的预期 CPU 时间。其中,p 指向一个 struct task_struct 结构体。

/* include/linux/sched.h */struct sched_entity {.../** 进程的虚拟运行时间 = 进程的实际运行时间 / 相对权重** 进程的 实际运行时间 是一段一段的,所以进程的 虚拟运行时间 也是一段一段增长的。** 进程的 虚拟运行时间 还会在 进程入队时 与 运行队列中的最小虚拟时间 相比较,如* 果更小的话会直接进行增加,并不对应 实际的运行时间。为什么要这么做呢?因为有的* 进程可能会因长时间睡眠,导致其 虚拟运行时间 小于运行队列中所有进程中的 最小虚* 拟运行时间,这样的进程一旦运行起来,会因为其 虚拟运行时间 小,占据 CPU 的时间* 就会长,对其它进程就不公平了。*/u64				vruntime;...
};struct task_struct {...const struct sched_class	*sched_class; /* 如果任务使用 CFS 调度,则为 &fair_sched_class */struct sched_entity		se; /* CFS 类进程调度参数 */...
};

在理想的 虚拟 CPU 上,在任何时刻,所有任务 虚拟运行时间值 p->se.vruntime 都保持相同的值。

CFS 调度器,其任务选择逻辑基于 p->se.vruntime 值:CFS 始终尝试运行具有最小 p->se.vruntime 值的任务(即到目前为止虚拟执行时间(virtual runtime)最小的任务)。CFS 始终尝试在可运行任务之间平均分配 虚拟 CPU时间 ,尽可能接近理想的多任务 虚拟 CPU

CFS 设计的其余部分大部分都脱离了这个非常简单的概念,有一些其它附加修饰,如任务的 nice 值,各种识别睡眠任务的算法等等。

2.3 运行队列:红黑树

CFS 不同于以往的调度器,它不使用运行队列以往的旧数据结构,而是使用按时间排序的 红黑树(rbtree) 来构建未来任务执行的时间线,而它之前 O(1) 调度器使用位图进行任务调度管理。

CFS 还维护 rq->cfs.min_vruntime 值,该值是一个单调递增值,用于跟踪运行队列所有任务中的最小 vruntime。该值用于尽可能将新激活的调度实体(struct sched_entity,即任务)置在红黑树左侧。由于该值一直使用 min_vruntime 单调递增累加,所以也可用来跟踪系统完成的工作总量。

/* kernel/sched/sched.h *//* CFS-related fields in a runqueue */
struct cfs_rq {...u64 min_vruntime; /* CFS 调度算法 运行队列中 进程 的 最小 虚拟运行时间 */...
};/** This is the main, per-CPU runqueue data structure.** Locking rule: those places that want to lock multiple runqueues* (such as the load balancing or the thread migration code), lock* acquire operations must be ordered by ascending &runqueue.*/
struct rq {...struct cfs_rq cfs;...
};DECLARE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues); /* 每 CPU 的运行队列 */

运行队列的权重通过 rq->cfs.load 值进行统计,该值是运行队列上排队的任务权重的总和:

/* kernel/sched/sched.h *//* CFS-related fields in a runqueue */
struct cfs_rq {/** 运行队列的权重, 等于其上所有进程的权重之和。* 进程在入队出队时,也会相应地从运行队列中加上减去其自身的权重。*/struct load_weight load;...
};

CFS 维护一个按时间排序的 红黑树(rbtree),即所有可运行的任务都按 p->se.vruntime 键排序,然后CFS 从这棵树中挑选最左边(即 p->se.vruntime 最小)的任务执行。随着时间往后推移,执行的任务越来越多地被放入树中,执行时间更少的任务逐渐往红黑树左边移动,执行时间更多的任务往红黑树右边移动,如此循环往复。

2.4 一些特征

CFS 使用纳秒级精度,对任务的虚拟运行时间的统计,它不依赖于 jiffiesHZ 值。因此,CFS 调度器没有像以前的调度器那样的时间片概念,也没有任何启发式方法。不使用启发式方法这一点,这使得 CFS 不容易受到针对启发式调度的攻击。

CFS任务 nice 值SCHED_BATCH 策略任务 的处理比以往的调度器更优。

CFSSMP 架构下的负载均衡代码进行了清理,使得负载均衡逻辑更加简单。

2.5 调度策略

CFS 实现了 3调度策略

  • SCHED_NORMAL,以往也叫 SCHED_OTHER:用于常规任务
  • SCHED_BATCH适用没有用户交互行为的后台进程,用户对该类进程的响应时间要求不高,但对吞吐量要求较高,因此调度器会在完成所有 SCHED_NORMAL 的任务之后让该类任务不受打扰地跑上一段时间,这样能够最大限度地利用缓存。
  • SCHED_IDLE:这类调度策略被用于系统中优先级最低的任务,只有在没有任何其他任务可运行时,调度器才会将运行该类任务。

2.6 调度器类别

Linux 同时支持多种类型调度器类别CFS 调度只是其中的一种,实现在文件 sched/fair.c 中。而像其它的调度类别如实现 SCHED_FIFOSCHED_RR 调度策略的实时调度器,实现在文件 sched/rt.c 中。

调度类是通过 struct sched_class 结构体实现的,该结构体包含一些列回调,在发生特定事件时被调用。struct sched_class 结构体内容如下:

/* kernel/sched/sched.h */struct sched_class {const struct sched_class *next;/** 将进程 @p 放入运行队列 @rq 。* 在任务进入可运行状态时调用。* 它将调度实体(任务)放入红黑树中,并递增 nr_running 变量。*/void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);/** 将进程 @p 移出运行队列 @rq 。* 当任务不再可运行时,将调用此函数以将相应的调度实体移出红黑树,并递减 * nr_running 变量。*/void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);/** 当前进程主动让出 CPU , 即移出运行队列,但 其状态依然是 runnable ,* 然后将另一进程加入运行队列。* 可通过系统调用 sys_sched_yield() 触发。*/void (*yield_task) (struct rq *rq);/* 当前进程主动让出 cpu 给进程组内进程 @p */bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt);/** 被唤醒进程的抢占逻辑:检查 @p 是否会抢占 @rq 中当前正在运行的进程. * 通常情况下是在 @p 进入 runnable 态时,检查 @p 是否会抢占当前正在运* 行的进程。*/void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags);/** It is the responsibility of the pick_next_task() method that will* return the next task to call put_prev_task() on the @prev task or* something equivalent.** May return RETRY_TASK when it finds a higher prio class has runnable* tasks.*//* * 挑选下一可执行进程, 且以 @prev 为参数, 调用 put_prev_task() . ** 通常返回挑选的下一可执行进程, 但当发现更高优先级调度类别有可运行进程时, * 返回 RETRY_TASK .*/ struct task_struct * (*pick_next_task) (struct rq *rq,struct task_struct *prev,struct rq_flags *rf);void (*put_prev_task) (struct rq *rq, struct task_struct *p);#ifdef CONFIG_SMP/** 为进程 @p 选择运行队列。* 当系统调用 fork() + exec() 创建一个新的进程时,在 SMP 系统中* 选择一个合理的 runqueue 来将该进程入队。Scheduler 此时需要考* 虑 负载均衡 问题。*/int  (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags);void (*migrate_task_rq)(struct task_struct *p);void (*task_woken) (struct rq *this_rq, struct task_struct *task);/* 设置进程的 cpu affinity */void (*set_cpus_allowed)(struct task_struct *p, const struct cpumask *newmask);void (*rq_online)(struct rq *rq); /* cpu online */void (*rq_offline)(struct rq *rq); /* cpu offline */
#endifvoid (*set_curr_task) (struct rq *rq);void (*task_tick) (struct rq *rq, struct task_struct *p, int queued);void (*task_fork) (struct task_struct *p);void (*task_dead) (struct task_struct *p);/** The switched_from() call is allowed to drop rq->lock, therefore we* cannot assume the switched_from/switched_to pair is serliazed by* rq->lock. They are however serialized by p->pi_lock.*/void (*switched_from) (struct rq *this_rq, struct task_struct *task); /* @task 从 当前调度类别 切换到 其他调度类别 */void (*switched_to) (struct rq *this_rq, struct task_struct *task); /* @task 从 其他调度类别 切换到 当前调度类别 */void (*prio_changed) (struct rq *this_rq, struct task_struct *task, /* 优先级变更时的抢占逻辑 */int oldprio);/* 获取分配给进程 @task 的时间片 (sys_sched_rr_get_interval()) */unsigned int (*get_rr_interval) (struct rq *rq, struct task_struct *task);/* 更新当前进程运行时间统计信息 */void (*update_curr) (struct rq *rq);#define TASK_SET_GROUP  0
#define TASK_MOVE_GROUP	1#ifdef CONFIG_FAIR_GROUP_SCHEDvoid (*task_change_group) (struct task_struct *p, int type);
#endif
};

CFS 调度器类别定义如下:

/** All the scheduling class methods:*/
const struct sched_class fair_sched_class = {.next			= &idle_sched_class,.enqueue_task		= enqueue_task_fair,.dequeue_task		= dequeue_task_fair,.yield_task		= yield_task_fair,.yield_to_task		= yield_to_task_fair,.check_preempt_curr	= check_preempt_wakeup,.pick_next_task		= pick_next_task_fair,.put_prev_task		= put_prev_task_fair,#ifdef CONFIG_SMP.select_task_rq		= select_task_rq_fair,.migrate_task_rq	= migrate_task_rq_fair,.rq_online		= rq_online_fair,.rq_offline		= rq_offline_fair,.task_dead		= task_dead_fair,.set_cpus_allowed	= set_cpus_allowed_common,
#endif.set_curr_task          = set_curr_task_fair,.task_tick		= task_tick_fair, /* cfs 类定时器周期调度接口: scheduler_tick() -> task_tick_fair() */.task_fork		= task_fork_fair, /* 子进程创建时, 更新其虚拟时间 */.prio_changed		= prio_changed_fair,.switched_from		= switched_from_fair,.switched_to		= switched_to_fair,.get_rr_interval	= get_rr_interval_fair,.update_curr		= update_curr_fair,#ifdef CONFIG_FAIR_GROUP_SCHED.task_change_group	= task_change_group_fair,
#endif
};

2.7 扩展:组调度

通常,CFS 调度器对单个任务进行操作,并努力为每个任务提供公平的 CPU 时间。有时,可能需要对任务进行分组,并为每个此类任务组提供公平的 CPU 时间。例如,可能需要首先为系统上的每个用户提供公平的 CPU 时间,然后再为属于用户的每个任务提供公平的 CPU 时间。

CONFIG_CGROUP_SCHED 配置涵盖的功能,试图实现这一目标:它允许对任务进行分组,并在这些组之间公平地分配 CPU 时间。

CONFIG_RT_GROUP_SCHED 允许对实时任务(即使用 SCHED_FIFOSCHED_RR 调度策略的任务)进行分组。

CONFIG_FAIR_GROUP_SCHED 允许对 CFS 任务(即使用 SCHED_NORMALSCHED_BATCH 调度策略的任务)进行分组。

开启了 CONFIG_FAIR_GROUP_SCHED 后,将为使用 cgroups 伪文件系统创建的每个组创建一个 cpu.shares 文件。请参阅以下示例步骤,以创建任务组并使用 cgroups 伪文件系统 修改 CPU 份额

# mount -t tmpfs cgroup_root /sys/fs/cgroup
# mkdir /sys/fs/cgroup/cpu
# mount -t cgroup -ocpu none /sys/fs/cgroup/cpu
# cd /sys/fs/cgroup/cpu# mkdir multimedia      # create "multimedia" group of tasks
# mkdir browser         # create "browser" group of tasks# #Configure the multimedia group to receive twice the CPU bandwidth
# #that of browser group# echo 2048 > multimedia/cpu.shares
# echo 1024 > browser/cpu.shares# firefox &     # Launch firefox and move it to "browser" group
# echo <firefox_pid> > browser/tasks# #Launch gmplayer (or your favourite movie player)
# echo <movie_player_pid> > multimedia/tasks

以上这一系列操作,分别为 多媒体程序浏览器程序 创建了两个任务分组,并指定了它们各自的 CPU 配额

注意,以上这些配置项都依赖于 CONFIG_CGROUPS,并允许管理员使用 cgroup 伪文件系统创建任务组。更多 cgroups 功能,读者可查阅相关资料。

3. 参考资料

[1] CFS Scheduler
[2] Completely Fair Scheduler
[3] 2.2.3 核心概念 - 调度策略

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

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

相关文章

探索磁力搜索引擎:互联网资源获取的新视角

在当今数字化社会中&#xff0c;寻找和获取网络资源变得更加便捷和多样化。磁力搜索引擎作为这一趋势的一部分&#xff0c;提供了一种新颖而有效的方法来定位和获取用户所需的文件、媒体和其他数字内容。本文将深入探讨磁力搜索引擎的工作原理、使用场景及其在网络文化中的影响…

最快安装zabbix

部署zabbix 6.x 建议使用红帽系统。 https://download.rockylinux.org/pub/rocky/8/isos/x86_64/Rocky-8.9-x86_64-minimal.iso1> 配置安装yum源 [rootzabbix ~]# yum install https://mirrors.huaweicloud.com/zabbix/zabbix/6.2/rhel/8/x86_64/zabbix-release-6.2-3.el8…

Spring框架常用注解(Annotation)收录

Spring框架常用注解(Annotation)收录 Spring框架广泛使用注解来配置和管理Bean&#xff0c;简化了XML配置&#xff0c;提高了开发效率。以下是一些Spring框架中常用的注解&#xff1a; 1. Component, Service, Repository, Controller 用途&#xff1a;这些是组件扫描注解&am…

`kubectl get pod -oyaml` 和 `kubectl describe pod`

kubectl get pod -oyaml 和 kubectl describe pod 这两个命令都用于获取 Pod 的信息&#xff0c;但它们提供信息的方式和内容有所不同&#xff1a; kubectl get pod -oyaml&#xff1a; 这个命令列出指定 Pod 的信息&#xff0c;输出格式为 YAML。输出内容是结构化的&#xff0…

精度丢失引起的支付失败问题

问题描述 在提交订单时候&#xff0c;输入充值金额和优惠码&#xff0c;后台会返回具体的订单信息&#xff0c;如下图&#xff0c;支付金额应该是1 * (1 - 0.09) 0.91&#xff08;这个是理想状态&#xff09;&#xff0c;但是表单显示的是0.90999997&#xff0c; 然后点击确…

架构师篇-1、总体架构设计

业务架构哲学本质 定位&#xff1a;赋予业务架构设计能力&#xff0c;具备业务架构设计思维模型&#xff0c;掌握业务架构哲学本质&#xff0c;形成以不变应万变的业务架构设计能力。 架构师所需要的能力&#xff1a; 带领业务成功通过框架思维赋能业务架构师知识体系构建掌…

PostgreSQL源码分析——pg_archivecleanup

pg_archivecleanup用于清理PostgreSQL WAL归档文件。指定归档目录&#xff0c;指定一个最老的日志段文件&#xff08;在此之前的WAL日志都删掉&#xff09;&#xff0c; 用法如下&#xff1a; postgresslpc:~$ pg_archivecleanup --help pg_archivecleanup removes older WAL …

HDU——2090.算菜价、2091.空心三角形、2093.考试排名

2090.算菜价 题目描述 Problem - 2090 Problem Description 妈妈每天都要出去买菜&#xff0c;但是回来后&#xff0c;兜里的钱也懒得数一数&#xff0c;到底花了多少钱真是一笔糊涂帐。现在好了&#xff0c;作为好儿子&#xff08;女儿&#xff09;的你可以给她用程序算一…

理解前端Cookie中的SameSite属性

SameSite属性是一个相对较新的Cookie属性&#xff0c;它可以帮助防止跨站请求伪造&#xff08;CSRF&#xff09;攻击。SameSite属性用于声明Cookie是否可以在跨站点情况下发送。 SameSite属性有三个可选值&#xff1a;Strict&#xff0c;Lax和None。 SameSiteStrict&#xff1…

Idea Git 解决Filename too long的问题

问题描述&#xff1a; 从远程厂库拉取代码以后&#xff0c;可能是被人的文件名字太长了&#xff0c;本地显示文件已经被删除。 原因&#xff1a; Windows系统下&#xff0c;在Git使用过程中&#xff0c;出现“filename too long”错误提示。直译成中文的意思就是&#xff1a;”…

Adobe XD是否收费?试试这几款超值的免费软件吧!

Adobe XD是一站式的 UX/UI 设计平台&#xff0c;设计师可以使用Adobe XD完成移动应用app界面设计、网页设计、原型设计等。Adobe XD也是一款结合原型和设计&#xff0c;提供工业性能的跨平台设计产品。而Adobebe。 XD跨平台的特点得到了很好的弥补 Sketch 没有 Windows 版本的缺…

网安人必备!开源网络安全工具TOP 10(附下载地址)

工欲善其事&#xff0c;必先利其器。对于广大的网络安全从业者&#xff0c;以及未来想要从事网络安全的人来说&#xff0c;选择并善用合适的网络安全工具&#xff0c;能有效提升工作效率。 开源网络安全工具之所以能够在众多安全解决方案中脱颖而出&#xff0c;不仅是因为它们…

C++ 68 之 类模版作函数的参数

#include <iostream> // #include <cstring> #include <string> using namespace std;template<class T1, class T2> // 可以设置默认的类型值&#xff0c;后面在使用的时候&#xff0c;就不用再指定类型了 class Students08{ public:T1 m_name;T2 m_a…

SysTools MailXaminer: 电子邮件取证调查中的链接分析和时间线分析

天津鸿萌科贸发展有限公司是 SysTools 系列软件的授权代理商。 SysTools MailXaminer 电子邮件取证软件提供全面强大的解决方案&#xff0c;通过简化的操作&#xff0c;从电子邮件客户端、网络邮箱服务器、磁盘镜像、Skype 通讯工具中解密并搜索证据。软件对调查工作的每一阶段…

c++参考std::string自己设计类hstring

目录 一、前言 二、设计需求 三、设计思想 1.功能一 1.功能二 四、设计过程 1.类hstring搭建 2. 实现有参构造函数 3. 实现副本构造函数 4.完整代码 五、结束语 一、前言 在c中有很多的库&#xff0c;但是在有些时候呢&#xff0c;我们一定要学会自己去设计库&#…

PHP框架之symfony框架

Symfony框架详解 Symfony是一个由SensioLabs公司开发并维护的PHP框架&#xff0c;旨在提高开发效率、代码复用性和应用的可维护性。自2005年发布以来&#xff0c;Symfony已成为众多开发者的首选框架之一&#xff0c;尤其在构建复杂的Web应用程序方面表现出色。 主要特点 高效…

文件扫描工具都有哪些?职场大佬都在用的文本提取工具大盘点~

回想起刚毕业初入职场那阵子&#xff0c;领导让帮忙把纸质文件扫描提取为文本时&#xff0c;还只会傻乎乎地一点点操作&#xff0c;属实是费劲得很&#xff01; 好在后面受朋友安利&#xff0c;找到了4个能够快速实现文件扫描文字提取的方法&#xff0c;这才让我的办公效率蹭蹭…

关于自学\跳槽\转行做网络安全行业的一些建议

很好&#xff0c;如果你是被题目吸引过来的&#xff0c;那请看完再走&#xff0c;还是有的~ 为什么写这篇文章 如何自学入行&#xff1f;如何小白跳槽&#xff0c;年纪大了如何转行等类似问题 &#xff0c;发现很多人都有这样的困惑。下面的文字其实是我以前的一个回答&#…

当site-packages的类型为.so,Python解释器不会提示或列出该模块可用的函数和类的原因及解决方法

原因&#xff1a; 当你在Python中导入一个.so文件&#xff08;或者任何扩展模块&#xff09;时&#xff0c;如果它不提供任何Python级别的接口或文档&#xff0c;Python解释器通常不会提示或列出可用的函数和类。这是因为扩展模块可能是用C语言编写的&#xff0c;并且它们通常…

MyBatis-For input string: “oqm“ 异常

前言 具体的异常信息如下&#xff1a; Error attempting to get column open_id from result set. Cause: java.lang.NumberFormatException: For input string: "oqmJX5ZPU1KOv-YDt30GNAN-Zefk" 乍一看下其实就是无法把open_id字符串类型转为数字类型进行赋值&…