Linux内核分析(调度类和调度实体)

文章目录

  • 前言
  • 一、调度类
      • 1. `stop_sched_class`
      • 2. `dl_sched_class`
      • 3. `rt_sched_class`
      • 4. `fair_sched_class`
      • 5. `idle_sched_class`
      • 总结
  • 二、调度类中的操作函数
  • 三、调度实体


前言

调度是操作系统内核的一个关键职责,它涉及到如何合理分配CPU时间给不同的进程或线程。在Linux内核中,调度不仅仅是一个简单的任务切换过程,而是一个精细化管理的过程,涉及多个层次和多种策略。理解调度类(Scheduler Classes)和调度实体(Scheduling Entities)的工作机制,对于系统开发者和性能调优工程师来说,都是至关重要的。

一、调度类

在这里插入图片描述
在Linux内核中,调度类(sched_class)是调度系统中的核心组件,它们决定了任务(进程或线程)如何被调度和管理。Linux内核支持多个调度类,每个调度类都具有不同的调度策略和行为,以满足不同的系统需求。以下是五个主要的调度类及其简要说明:

1. stop_sched_class

  • 功能stop_sched_class 用于处理被停止的任务。它通常用于进程在被停止或挂起时的调度管理。这类任务不会被调度,主要用于保存进程的状态以便恢复。
  • 特点:任务在这个调度类中不会被分配CPU时间。它的调度函数不会被调用,调度系统会忽略这个类的任务。

2. dl_sched_class

  • 功能dl_sched_class 是"Deadline Scheduling"(截止时间调度)的调度类。它用于实时任务的调度,特别是那些有严格时间要求的任务。
  • 特点:截止时间调度旨在确保任务在其截止时间之前完成。这个调度类使用基于截止时间的算法来决定任务的优先级,并确保实时任务的时效性。

3. rt_sched_class

  • 功能rt_sched_class 是"Real-Time Scheduling"(实时调度)的调度类。它处理具有实时需求的任务,如音频处理或控制系统中的任务。
  • 特点:实时调度类提供了几种不同的调度策略,如FIFO(先进先出)和RR(轮询),确保实时任务能够在可预测的时间内得到执行。这个类的任务通常具有比其他调度类更高的优先级。

4. fair_sched_class

  • 功能fair_sched_class 是"Completely Fair Scheduler"(完全公平调度)的调度类。它用于处理普通任务的调度,试图在所有任务之间公平地分配CPU时间。
  • 特点:完全公平调度(CFS)旨在提供一种公平的调度方式,以避免任务饥饿,并尽量均匀地分配CPU时间。CFS使用红黑树来跟踪任务的执行情况,并基于虚拟运行时间来决定调度顺序。

5. idle_sched_class

  • 功能idle_sched_class 处理空闲任务。在系统没有其他任务需要调度时,空闲调度类负责运行系统空闲任务。
  • 特点:这个调度类的任务通常是系统空闲的地方(idle task),用于在没有其他任务时降低CPU功耗。它的调度策略通常是低优先级的,以确保CPU在其他任务运行时能够被及时分配。

总结

每个调度类在Linux内核中扮演着不同的角色,从处理实时任务到公平分配CPU时间,再到处理系统空闲任务。调度类的设计和实现保证了系统能够有效地处理各种不同类型的任务,满足不同的性能和响应需求。理解这些调度类有助于深入掌握Linux内核的调度机制,并进行更好的系统优化和调试。

二、调度类中的操作函数

在这里插入图片描述

const struct sched_class fair_sched_class = {/* 调度类的下一个调度类(idle_sched_class)*/.next = &idle_sched_class,/* 将任务加入调度队列的函数(公平调度队列) */.enqueue_task = enqueue_task_fair,/* 从调度队列中移除任务的函数(公平调度队列) */.dequeue_task = dequeue_task_fair,/* 任务自愿让出 CPU 的函数(公平调度) */.yield_task = yield_task_fair,/* 任务让出 CPU 给特定任务的函数(公平调度) */.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,/* 设置下一个任务的函数(公平调度) */.set_next_task = set_next_task_fair,#ifdef CONFIG_SMP/* 在多处理器系统中,平衡负载的函数(公平调度) */.balance = balance_fair,/* 选择任务的 CPU 运行队列的函数(公平调度) */.select_task_rq = select_task_rq_fair,/* 将任务迁移到新的 CPU 运行队列的函数(公平调度) */.migrate_task_rq = migrate_task_rq_fair,/* 处理 CPU 运行队列上线事件的函数(公平调度) */.rq_online = rq_online_fair,/* 处理 CPU 运行队列下线事件的函数(公平调度) */.rq_offline = rq_offline_fair,/* 处理任务死亡事件的函数(公平调度) */.task_dead = task_dead_fair,/* 设置任务允许使用的 CPU 的函数(公平调度) */.set_cpus_allowed = set_cpus_allowed_common,
#endif/* 处理任务时间片到期的函数(公平调度) */.task_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#ifdef CONFIG_UCLAMP_TASK/* 是否启用任务 UClamp(用于任务 CPU 能力调整) */.uclamp_enabled = 1,
#endif
};

主要函数解析:

enqueue_task_fair函数:

这个函数负责将一个新的或唤醒的任务添加到 CFS 的调度队列中

static void
enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
{struct cfs_rq *cfs_rq;struct sched_entity *se = &p->se;int idle_h_nr_running = task_has_idle_policy(p);/** 将任务的预估利用率添加到 cfs_rq 的预估利用率中,* 然后更新调度器的频率选择。*/util_est_enqueue(&rq->cfs, p);/** 如果任务正在进行 I/O 等待,这里显式更新 CPU 频率利用率。*/if (p->in_iowait)cpufreq_update_util(rq, SCHED_CPUFREQ_IOWAIT);/* 遍历调度实体 */for_each_sched_entity(se) {/* 如果任务已经在运行队列中,则跳过 */if (se->on_rq)break;/* 获取任务所属的 cfs_rq */cfs_rq = cfs_rq_of(se);/* 将任务实体加入到 cfs_rq 中 */enqueue_entity(cfs_rq, se, flags);/** 如果遇到被限制的 cfs_rq,则中止后续操作* 注意:遇到被限制的 cfs_rq 时,我们将在后面增加最终的 h_nr_running 计数*/if (cfs_rq_throttled(cfs_rq))break;/* 更新 cfs_rq 的运行任务数量 */cfs_rq->h_nr_running++;cfs_rq->idle_h_nr_running += idle_h_nr_running;/* 标记为唤醒操作 */flags = ENQUEUE_WAKEUP;}/* 再次遍历调度实体 */for_each_sched_entity(se) {cfs_rq = cfs_rq_of(se);cfs_rq->h_nr_running++;cfs_rq->idle_h_nr_running += idle_h_nr_running;if (cfs_rq_throttled(cfs_rq))break;/* 更新负载平均值 */update_load_avg(cfs_rq, se, UPDATE_TG);update_cfs_group(se);}/* 如果没有找到调度实体 */if (!se) {add_nr_running(rq, 1);/** 新任务的初始 util_avg 设为 CPU 空闲容量的一半,* 对于小任务,可能会跨越超负荷阈值,这会影响负载均衡器的任务放置。* 为了缓解这个问题,不在超负荷状态检测中计入新任务的第一次入队操作。*/if (flags & ENQUEUE_WAKEUP)update_overutilized_status(rq);}/* 如果启用了带宽控制 */if (cfs_bandwidth_used()) {/** 带宽控制启用时,上述操作中断可能会导致叶子列表维护不完全,* 触发以下断言。*/for_each_sched_entity(se) {cfs_rq = cfs_rq_of(se);if (list_add_leaf_cfs_rq(cfs_rq))break;}}assert_list_leaf_cfs_rq(rq);/* 更新高精度定时器 */hrtick_update(rq);
}

dequeue_task_fair函数:

将任务从 CFS 调度队列中移除

static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)
{struct cfs_rq *cfs_rq;struct sched_entity *se = &p->se;int task_sleep = flags & DEQUEUE_SLEEP;  // 检查任务是否因休眠而被删除int idle_h_nr_running = task_has_idle_policy(p);  // 任务是否有空闲策略bool was_sched_idle = sched_idle_rq(rq);  // 检查之前的调度队列是否为空闲队列// 遍历任务的调度实体,进行任务的移除for_each_sched_entity(se) {cfs_rq = cfs_rq_of(se);  // 获取调度队列dequeue_entity(cfs_rq, se, flags);  // 从调度队列中移除任务/** 如果遇到被限制的 cfs_rq(调度队列当前受限,不能接受更多任务),* 则中止后续操作。*/if (cfs_rq_throttled(cfs_rq))break;// 更新运行任务的数量cfs_rq->h_nr_running--;cfs_rq->idle_h_nr_running -= idle_h_nr_running;// 如果调度队列还有其他实体,处理父实体if (cfs_rq->load.weight) {// 避免对当前实体进行重新负载评估se = parent_entity(se);/** 如果任务在其调度时间片内处于休眠状态,且没有被限制,* 则调整下一个选择的实体以偏向当前 cfs_rq。*/if (task_sleep && se && !throttled_hierarchy(cfs_rq))set_next_buddy(se);break;}flags |= DEQUEUE_SLEEP;  // 设置 DEQUEUE_SLEEP 标志}// 遍历任务的调度实体,更新调度队列for_each_sched_entity(se) {cfs_rq = cfs_rq_of(se);cfs_rq->h_nr_running--;cfs_rq->idle_h_nr_running -= idle_h_nr_running;if (cfs_rq_throttled(cfs_rq))break;// 更新负载平均值和 cfs 组update_load_avg(cfs_rq, se, UPDATE_TG);update_cfs_group(se);}// 如果没有其他调度实体,更新运行任务的总数if (!se)sub_nr_running(rq, 1);// 如果之前的调度队列非空闲且当前是空闲队列,则提前平衡调度if (unlikely(!was_sched_idle && sched_idle_rq(rq)))rq->next_balance = jiffies;// 更新利用率信息util_est_dequeue(&rq->cfs, p, task_sleep);// 更新高精度定时器hrtick_update(rq);
}

yield_task_fair函数:

在公平调度器中处理任务的主动让渡

static void yield_task_fair(struct rq *rq)
{struct task_struct *curr = rq->curr;  // 当前正在运行的任务struct cfs_rq *cfs_rq = task_cfs_rq(curr);  // 当前任务所属的 CFS 调度队列struct sched_entity *se = &curr->se;  // 当前任务的调度实体/** 如果调度队列中只有当前任务,直接返回。* 这意味着没有其他任务需要调度或执行。*/if (unlikely(rq->nr_running == 1))return;// 清除与当前任务相关的 buddy 标记(可能是调度优化相关的标记)clear_buddies(cfs_rq, se);// 如果当前任务不是批处理任务,则更新运行时统计信息if (curr->policy != SCHED_BATCH) {// 更新调度队列的时间戳update_rq_clock(rq);/** 更新当前任务的运行时间统计信息。* 这是为了保持当前任务的运行时间数据的准确性。*/update_curr(cfs_rq);/** 通知 `update_rq_clock()` 函数,已经进行了更新,* 以避免在 `schedule()` 函数中进行额外的微小更新时间,* 这样可以避免调度路径的双倍开销。*/rq_clock_skip_update(rq);}// 将当前任务标记为跳过 buddy 调度,这可能是调度优化的一部分set_skip_buddy(se);
}
	.pick_next_task		= __pick_next_task_fair,.put_prev_task		= put_prev_task_fair,.set_next_task          = set_next_task_fair,

__pick_next_task_fair

  • 作用:选择下一个任务进行调度。
  • 说明:这是公平调度器(CFS)的核心函数之一,用于从调度队列中选择下一个要执行的任务。它根据任务的优先级和运行时间等因素,决定哪个任务应当获得 CPU 时间。这一过程涉及到对任务的权重、运行时间等参数的评估,以保证系统的公平性。

put_prev_task_fair

  • 作用:处理当前任务(之前的任务)的状态更新。
  • 说明:在任务切换之前调用,用于处理当前任务的状态。主要任务是更新当前任务的运行时间数据和调度队列的状态。具体来说,它可能会更新任务的调度实体信息(如运行时间、优先级等),以便公平调度器在下一次调度时能更准确地评估任务的状态。

set_next_task_fair

  • 作用:设置下一个任务的状态。
  • 说明:在调度过程中,设置下一个要执行的任务的相关状态。这个函数负责初始化或配置即将调度的任务,以便它能顺利地开始执行。它可能会涉及到更新任务的调度实体状态以及其他调度相关的设置。

这些函数共同协作,确保公平调度器能高效且准确地管理任务的调度,保证系统资源的合理分配。

三、调度实体

在每一个task_struct结构体中都会有调度实体:
在这里插入图片描述
在这里插入图片描述
struct sched_entity 是 Linux 内核调度器中用于描述调度实体(任务或任务组)的数据结构。调度实体可以是一个单独的任务,也可以是一个任务组(当启用组调度时)。它的作用是跟踪调度相关的状态信息和统计数据。

struct sched_entity {/* For load-balancing: */struct load_weight	load;                    // 任务的负载权重,用于负载均衡unsigned long		runnable_weight;          // 任务的可运行权重struct rb_node		run_node;                 // 红黑树节点,用于调度队列管理struct list_head	group_node;               // 任务组的链表节点(当启用组调度时)unsigned int		on_rq;                   // 任务是否在运行队列上(标志位)u64			exec_start;                // 任务开始执行的时间戳u64			sum_exec_runtime;         // 任务自创建以来总的执行时间u64			vruntime;                 // 虚拟运行时间,用于公平调度u64			prev_sum_exec_runtime;    // 上一个时间片结束时的执行时间总和u64			nr_migrations;             // 任务迁移次数struct sched_statistics	statistics;           // 任务的调度统计信息#ifdef CONFIG_FAIR_GROUP_SCHEDint			depth;                    // 任务组的深度,用于层次调度struct sched_entity	*parent;                // 任务组的父任务(或任务组)/* rq on which this entity is (to be) queued: */struct cfs_rq		*cfs_rq;                 // 当前调度实体所在的 CFS 运行队列/* rq "owned" by this entity/group: */struct cfs_rq		*my_q;                   // 当前调度实体(或任务组)拥有的 CFS 运行队列
#endif#ifdef CONFIG_SMP/** Per entity load average tracking.** Put into separate cache line so it does not* collide with read-mostly values above.*/struct sched_avg	avg;                    // 每个调度实体的负载平均值,用于平衡多处理器系统中的负载
#endif
};

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

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

相关文章

uniapp打包H5的时候 清楚缓存(不安装依赖的前提下)

问题 在写项目的时候,打包好一个H5 发布成功,后来又重新打包新的包进行更新迭代,但是用户手机上还是上一个版本,本地缓存还是没有清除。 解决问题 步骤一:html不缓存 在html中,解决缓存的方法主要是依赖…

Keepalived学习

环境准备:两台服务器,两台客户机,关闭火墙和selinux 在两台主机上安装ka yum install keepalived -y 开启软件 keepalived配置 进入文件 vim /etc/keepalived/keepalived.conf 修改配置 配置slave 效果 在另一台路由配置 抢占模式和非…

简洁清新个人博客网页模板演示学习

30多款个人博客、个人网站、个人主页divcss,html在线预览,个人静态页面模板(wordpress、帝国cms、typecho主题模板).这些简洁和优雅的博客网页模板,为那些想成为创建博客的个人或媒体提供灵感设计。网页模板可以记录旅游、生活方式、食品或摄影博客等网站。部分网页模板来源网友…

二叉树进阶之二叉搜索树:一切的根源

前言: 在学完了简单的容器与C面向对象的三大特性之后,我们首先接触的就是map与set两大容器,但是这两个容器底层实现的原理是什么呢?我们不而知,今天,主要来为学习map与set的底层原理而打好基础&#xff0c…

面试官:Java虚拟机是什么,Java虚拟机的内存模型是什么样子的?

哈喽!大家好,我是小奇,一个专给面试官添堵的撑序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新,可以微信搜索【小奇JAVA面试】第一…

基于WEB的旅游推荐系统设计与实现

TOC springboot280基于WEB的旅游推荐系统设计与实现 第1章 绪论 1.1选题动因 当前的网络技术,软件技术等都具备成熟的理论基础,市场上也出现各种技术开发的软件,这些软件都被用于各个领域,包括生活和工作的领域。随着电脑和笔…

SVN权限控制解析

一、基础数据说明 1. 代码目录存在多级 图1-1 SVN目录 如图1-1: 第一级目录是 科顺,代表 科顺项目,项目文件包括 文档、代码等等。第二级目录 分别是 2.1 resources 用于存放文档,开发和实施均需…

Qt登录窗口

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget),btn(new QPushButton("取消", this)),login_btn(new QPushButton("登录", this)) { ui->setupUi(this);thi…

Llama 3.1深度解析:405B、70B及8B模型的多语言与长上下文处理能力

Llama 3.1 发布了!今天我们迎来了 Llama 家族的新成员 Llama 3.1 进入 Hugging Face 平台。我们很高兴与 Meta 合作,确保在 Hugging Face 生态系统中实现最佳集成。Hub 上现有八个开源权重模型 (3 个基础模型和 5 个微调模型)。 Llama 3.1 有三种规格: …

法线纹理贴图计算(切线空间世界空间)

效率: 在切线空间中计算,效率更高,因为可以在顶点着色器中就完成对光照、视角方向的矩 阵变换,计算量相对较小。( 矩阵变换在顶点着色器中计算) 在世界空间中计算,效率较低,由于需要对法线贴图进行采样&a…

mybatis druid postgresql statement超时原理原理

yaml设置超时 mybatis-plus:mapper-locations: classpath:/mapper/*.xml # MyBatis Mapper XML文件的位置type-aliases-package: com.company.mi.entity # 实体类所在的包configuration:default-statement-timeout: 10 configuration 设置超时 BaseStatementHandler设置超时 …

Thread 类的基本用法

目录 什么是线程? 编写多线程程序 线程创建的方式 继承 Thread 类,重写 run 方法 实现 Runnable 接口,重写 run 方法 匿名内部类创建 Thread 子类 匿名内部类创建 Runnable 子类对象 lambda表达式 Thread 类和常用方法 Thread 的常…

node.js part1

Node.js Node.js 是一个跨平台JavaScript 运行环境,使开发者可以搭建服务器端的 JavaScript 应用程序。作用:使用Node.js编写服务器端程序 编写数据接口,提供网页资源浏览功能等等 前端工程化:为后续学习Vue和React等框架做铺垫. …

基于CDIO概念的人工智能物联网系统开发与实施的人才培养研究

目录 1. 引言(Introduction) 2. AIoT技术及其培训特点(The Characteristics of AIOT and Its Training) 3. 基于CDIO概念的AIoT课程改革(CDIO Concept-based Reform of AIOT Course) 4. AIoT课程内容安…

Idea里配置Maven版本

一、安装Maven 1. 官网下载maven地址: Maven – Download Apache Maven Binary是可执行版本,已经编译好可以直接使用。 Source是源代码版本,需要自己编译成可执行软件才可使用。tar.gz和zip两种压缩格式,其实这两个压缩文件里面包含的内容是…

Verilog刷题笔记50

题目: Given the following state machine with 1 input and 2 outputs: 解题: module top_module(input in,input [9:0] state,output [9:0] next_state,output out1,output out2);assign next_state[0]~in&(state[0]|state[1]|state[2]|state[3]…

Java方法01:什么是方法

本节视频链接:Java方法01:什么是方法?_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV12J41137hu?p45&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 Java中的‌方法‌是一段执行特定任务的代码片段,‌它是程序的基本构…

C#中的S7协议

S7协议-S7COMM S7COMM 进行写 CTOP->PDU type已知枚举值 0X0E连接请求0x0d连接确认0x08断开请求0x0c断开确认0x05拒绝访问0x01加急数据0x02加急数据确认0x04用户数据0x07TPDU错误0x0f数据传输 S7Header->ROSCTR已知枚举值 0X01JOB REQUEST。主站发送请求0x02Ack。从站…

Android MediaRecorder 视频录制及报错解决

目录 一、start failed: -19 二、使用MediaRecorder录制视频 2.1 申请权限 2.2 布局文件 2.3 MediaRecordActivity 2.4 运行结果 三、拓展 3.1 录制视频模糊(解决) 3.2 阿里云OSS上传文件 3.2.1 权限(刚需) 3.2.2 安装SDK 3.2.3 使用 相关链接 一、start failed…

基于spring boot的小型诊疗预约平台的设计与开发

TOC springboot262基于spring boot的小型诊疗预约平台的设计与开发 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大,随着当前时代的信息化,科学化发展,让社会各行业领域都争相使用新的信息技术,对行业内的各种相关数据进…