一文带你弄懂linux进程调度算法--CFS

本文介绍了Linux操作系统中的完全公平调度程序(CFS),旨在模拟一个理想的多任务CPU,确保系统对所有任务公平。CFS使用红黑树数据结构和公平时钟,跟踪每个进程的CPU份额,根据等待时间进行调度,以减少进程等待时间。CFS还介绍了模块化调度器,允许不同调度策略的实现,如实时调度策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大多数现代操作系统旨在尝试从底层硬件资源中提取最佳性能。这主要是通过两个主要硬件资源的虚拟化来实现的:CPU 和内存。现代操作系统提供了一个多任务环境,基本上为每个任务提供了自己的虚拟 CPU。任务通常不知道它不独占 CPU 使用权这一事实。

类似地,内存虚拟化是通过给每个任务自己的虚拟内存地址空间来实现的,然后将其映射到系统的真实内存上。同样,任务通常不知道它的虚拟内存地址可能不会映射到实内存中的相同物理地址。

CPU 虚拟化是通过在多个任务之间“共享”CPU 来实现的——也就是说,每个正在运行的任务定期获得一小部分 CPU。从多个可用的可运行任务中一次选择一个任务的算法称为调度器,选择下一个任务的过程称为调度。

调度程序是任何操作系统最重要的组件之一。由于几个原因,实现调度算法很困难。首先,一个可接受的算法必须分配 CPU 时间,这样优先级较高的任务(例如 Web 浏览器等交互式应用程序)优先于低优先级任务(例如程序编译等非交互式批处理)。同时,调度程序必须防止低优先级进程饥饿。换句话说,无论有多少高优先级进程在争夺 CPU 时间,最终都必须允许低优先级进程运行。调度程序也必须精心设计,以便进程看起来同时运行,而不会对系统吞吐量产生太大影响。

对于像 GUI 这样的交互式进程,理想的调度程序会给每个进程在 CPU 上占用很少的时间,并在进程之间快速循环。因为用户希望交互式进程立即响应输入,所以理想情况下,用户输入和进程执行之间的延迟应该是人类察觉不到的——最多在 50 到 150 毫秒之间。

对于非交互式进程,情况正好相反。进程之间的切换或上下文切换是一项相对昂贵的操作。因此,处理器上更多的时间片和更少的上下文切换可以提高系统性能和吞吐量。调度算法必须在所有这些相互竞争的需求之间取得平衡。

像大多数现代操作系统一样,Linux 是一个多任务操作系统,因此它有一个调度程序。 Linux 调度程序随着时间的推移而发展。

1. O(1) 调度器

随着内核 2.6 的发布,Linux 调度程序被彻底改革。这个新的调度器被称为 O(1) 调度器——O(…) 被称为“大 O 表示法”。选择这个名称是因为调度程序的算法需要恒定的时间来做出调度决策,而不管任务数量如何。 O(1) 调度器使用的算法依赖于活动和过期的进程数组来实现恒定的调度时间。每个进程都有一个固定的时间段,之后它被抢占并移动到过期的数组中。一旦活动阵列中的所有任务都用完它们的时间片并移至过期阵列,就会发生阵列切换。此开关使活动阵列成为新的空过期阵列,而过期阵列成为活动阵列。

该算法的主要问题是用于将任务标记为交互式或非交互式的复杂启发式方法。该算法试图通过分析平均睡眠时间(进程等待输入所花费的时间)来识别交互式进程。长时间休眠的进程可能正在等待用户输入,因此调度程序假定它们是交互式的。调度程序为交互式任务提供优先级奖励(以获得更好的吞吐量),同时通过降低非交互式任务的优先级来惩罚非交互式任务。用于确定任务交互性的所有计算都很复杂,并且容易发生潜在的错误计算&

### 完全公平调度器(CFS)原理 #### CFS概述 Completely Fair Scheduler(简称CFS),即完全公平调度器,是Linux系统中负责普通进程调度的核心组件之一[^2]。该调度器旨在提供一种更加公正的方式分配处理器资源给各个线程或进程。 #### 数据结构与时间复杂度 为了高效地管理和查找具有最低`vruntime`(虚拟运行时间)的任务,CFS选择了红黑树作为内部数据结构的一部分。这种选择使得每次插入、删除操作都能保持在O(log n),从而保证了良好的性能表现[^1]。 #### vruntime机制 CFS引入了一个名为`vruntime`的概念来衡量每个任务已经获得的服务量。当新创建一个任务时会赋予其初始值;随着程序执行进度增加而累加此参数。对于等待更长时间未被执行过的轻载任务来说,则会被给予较低的`vruntime`值并放置于红黑树左侧位置,以便尽快得到CPU服务机会。 #### 关键函数解析 - **enqueue_entity()**: 将指定的任务加入到当前活动列表(Active List)以及对应的RB Tree节点之中; - **dequeue_entity()**: 移除某个特定任务实例及其关联记录项; - **place_entity()**: 对即将进入系统的进程进行初始化设置,并调整其`vruntime`以确保公平性[^3]。 ```c void place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initialPlacement); ``` 这段代码展示了如何定义`place_entity()`方法签名,其中包含了两个主要输入参数:一个是代表控制组级别的请求队列对象(`cfs_rq`),另一个是指向待处理实体(se)的指针变量。 #### 执行流程示例 通过构建内核模块并加载至目标机器上来观察实际效果是一种常见做法。例如,在完成编译之后可以利用命令行工具如make和insmod配合使用,进而触发相应事件的发生和发展变化情况: ```bash make && sudo insmod km_param.ko pid1=<PID> pid2=<PID> dmesg | tail -n 50 ``` 上述脚本片段说明了怎样安装自定义驱动文件km_param.ko并将选定进程ID传递进去测试CFS行为模式,最后借助日志查看最近发生的动态信息[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值