Linux 调度器 BFS上

运维 系统运维
  BFS 是一款专门为 Linux 桌面环境所设计的内核调度器,它基于 Staircase Deadline 和 EEVDF 算法,支持 Linux 2.6.31 之后的内核。它提供了前所未有的流畅桌面性能,不仅得到了用户的认可,也为一些商业系统所采用。本文简单介绍了 BFS 的基本原理,希望能对想深入了解 Linux 调度器的读者提供一些帮助。

  BFS 是一个进程调度器,可以解释为“脑残调度器”。这古怪的名字有多重含义,比较容易被接受的一个说法为:它如此简单,却如此出色,这会让人对自己的思维能力产生怀疑。

  BFS 不会被合并进入 Linus 维护的 Linux mainline,BFS 本身也不打算这么做。但 BFS 拥有众多的拥趸,这只有一个原因:BFS 非常出色,它让用户的桌面环境达到了前所未有的流畅。在硬件越来越先进,系统却依然常显得迟钝的时代,这实在让人兴奋。

  进入 2010 年,Android 使用 BFS 作为其操作系统的标准调度器,这也证明了 BFS 的价值。

一、BFS vs CFS,性能测试比拼

  BFS 出现后得到了很多用户的好评,得到了诸如“快,感觉的到的快”,“桌面的急速未来”等评价。这些词让人侧目,于是我便开始四下寻找关于 BFS 的测试数据,希望能找到说明这一切的数字或者曲线。但结果却颇令人失望。。。

1.Jens Axboe 的测试

  BFS 发布后不久,即 2009 年 9 月,Ingo Molnar 发布了他的测评报告,比较了 CFS 和 BFS。作为 CFS 的作者 , 他所宣称的测试结果并不让人觉得意外:CFS 在各个方面优于 BFS。不过人们对他的测评结果有不同的反应,有人认同,也有人心存疑惑。Jens Axboe 就是心存怀疑的一位,他自己写了一个名为 Latt.c 的程序,试图测试调度器的两个神秘属性:”Interactivity”和 “Fluidness”。

  他的测试结果刚好相反,表明 BFS 在交互性方面优于 CFS,而且其 CPU 利用率更高。不过 BFS 稳定性较差,并且在某些情况下也表现出了糟糕的交互性问题。

  从 Jens 的测试数据来看,BFS 稍微优于 CFS,但优势并非如同坊间流传的那样夸张。感兴趣的读者可以在 lkml 的邮件列表中找到 Jens 测试的详细数据:http://thread.gmane.org/gmane.linux.kernel/886319/focus=887636

  结果让翘首以盼的我有些失望,并没有看到 BFS 遥遥领先。反而有些类似奥运会男子百米的决赛,究竟谁是冠军一时竟难以分辨。但值得注意的是,该测试意外地让人们认识到了 CFS 本身的一个严重问题。

  CFS 的 sleeper fairness 特性导致在一些情况下将出现严重的调度延迟,在 Jens 的 xmodmap测试中甚至出现了 10s 的延迟。并且围绕 Jens 的测试,人们纷纷发表声明,使用 CFS 时有很多交互性问题,比如编译内核时,同时的音频视频会出现严重的停顿,而使用 BFS 则没有这些问题。不过这些 CFS 的问题都在关闭了 sleeper fairness 特性后神秘地消失了。

  这让 CFS 调度器的开发者不得不暂时关闭了 sleeper fairness 特性,并一度曾号称将在即将发布的 2.6.32 中正式关闭该特性,直到问题被解决为止。令人吃惊的是,Ingo 在一周之内就抛出了新的 patch,即 Gentle Fairness。使用这个 patch,10s 延迟消失了,其他的关于鼠标滞后,视频停顿的关于 CFS 的负面报告也都消失了。。。

2.Phoronix 的测试

  您可以在 http://www.phoronix.com/scan.php?page=article&item=bfs_scheduler_benchmarks&num=1和 http://global.phoronix-test-suite.com/?k=profile&u=zero-9274-28890-6247看到 Phoronix 对 BFS 的专业测试。该测试也是在 2009 年 9 月完成的,如前所述,此后 BFS 和 CFS 都有了一些更新,因此该测试也不能完全反映这两款调度器最新的状态。但作为权威的测评机构,该测评结果还是值得一看。

  从 Phoronix 的测试结果来看,BFS 在多项测试中稍微领先,CFS 则在其余一些测试项目中反超。我不禁又有些黯然。

  唯一能体现 BFS“急速”的测试项目来自针对网络服务器吞吐量的测试,特在此处张贴这张最具有说服力和震撼力的直方图。

  图 1. 网络吞吐量测试

  

图 1. 网络吞吐量测试

 

  但除此一项之外,总的来讲,Phoronix 的测试结果终究只是表明 BFS 和 CFS 旗鼓相当。

3.University of New Mexico 计算机系的测评

  新墨西哥大学的 Taylor Groves, Je Knockel, Eric Schulte 在 2009 年 12 月也发布了一个 BFS vs. CFS 的评测报告。

  他们的测评关注于三个方面:延迟 , Turnaround Time 还有交互性。下面摘录他们的测试结果。

  图 2. 延迟

  

图 2. 延迟

 

  图 3. Turnaround Time

  

图 3. Turnaround Time

 

  图 4. 交互性

  

图 4. 交互性

 

  这三张图总算聊以安慰我四处找寻的辛苦,根据这个评测结果,终于可以得到这样的结论:

  在 turnaround time 方面,CFS 优于 BFS。但是 BFS 的调度延迟小于 CFS。这说明 BFS 更加适应于交互式应用环境。CFS 更加适合于批处理作业环境。这跟许多用户的体验相同。

  小结

  以上三个测评都是在 Linux2.6.32 发布前完成的。然而 CFS 在 Linux2.6.32 中引入了 GENTLE_FAIR_SLEEPERS 特性,正如 2.1 节中所说,这个 patch 据说是极大地提高了交互性。不幸的是,在那以后,却似乎再也没有人做关于 CFS 和 BFS 的比较测试了。因此在 Linux 已经进入 2.6.35 的时代,我们更无法轻易得出 BFS 和 CFS 孰优孰劣的结论。

  从另一方面讲,虽然专业评测没有显示出 BFS 的明显优势,但从 Internet 上能收集到的信息来看,大多数用户都觉得 BFS 能够显著地提高交互式应用的体验,这是一种个人的体验,比如鼠标的移动是否流畅等等。在这类体验中,两款调度器的差异却是相当大,这无法用前面的测试数据来加以说明。

  因此我认为,目前人们并没有理解影响交互性的真正原因,专业测试所关注的数据尚无法准确描述诸如“流畅”这类主观的感觉。因此,对于 BFS,我们不妨相信感觉一次吧。

  那么 BFS 究竟做了哪些改进,如果这些改进如此有效,为什么主流内核不愿意接纳 BFS 呢?

二、BFS vs CFS

1.设计上的不同

  白天 Con Kolivas 在医院里当麻醉师,为人们解除痛苦,业余的时候借 Linux 解除自己的痛苦。额,Kolivas 学习 Linux 并不是为了解决痛苦,我臆测而已。但据 Kolivas 自述,他接触 Linux 内核时连 C 语言也没有学习过。。。这个事实证明,语言只是一项工具,对问题本质的深入理解才是写程序的关键。可能还有执着,CFS 和 RSDL 之争导致 Kolivas 离开 Linux 社区,此去经年,当 Kolivas 再次开始看内核代码的时候,他立即发现 CFS 存在以下几个设计上的问题:

  CFS 的目标是支持从桌面到高端服务器的所有应用场景,这种大而全的设计思路导致其必须做一些实现上的折中,此外,那些只有在高端机器中才需要的特性将引入不必要的复杂代码。

  其次,为了维护多 CPU 上的公平性,CFS 采用了负载平衡机制,Kolivas 认为,这些复杂代码抵消了 per cpu queue 曾带来的好处。

  最后,主流内核的 CFS 还是对睡眠进程存在一些偏好,这意味着“不公平”。

2.设计目标的不同

  在现实中,调度算法类似一个处境尴尬的主妇,满足孩子对晚餐的要求便有可能伤害到老人的食欲。Linux 内核一直试图做出一道让全家老少都喜欢的菜,在这方面,CFS 已经做的很好。但一道能被所有人接受的菜,或许就意味着稍许平淡。而 BFS 只打算满足一种口味,以便将这种口味发展到极限。

  根据 Linux Magazine 的说法,Con Kolivas 是看到了下面这则来自 xkcd 的漫画而开始思考 BFS 的。

  图 5. 讥讽 Linux 调度器的 xkcd 漫画

  

图 5. 讥讽 Linux 调度器的 xkcd 漫画

 

  事情源于一些 Linux 用户,他们发现 Linux 虽然号称能够充分发挥 4096 颗 CPU 系统的计算能力,但在普通的 laptop 上却无法流畅地播放 Youtube 视频。

  这让人们开始思考,对于 Desktop 环境来讲,CFS 哪些复杂的特性究竟是否还有意义?人们是否有必要在自己的个人电脑中使用一个支持 4096 个 CPU 的调度器?

  BFS 正是对这种质疑的自然反应。它不打算支持 4096 个 CPU 的庞然大物,BFS 的目标是普通人使用的桌面电脑。此外,BFS 还删除了那些只有在服务器上才需要的特性。比如,BFS 抛弃了 CFS 的组调度特性,类似 CGROUP 这样的特性对于普通的桌面用户是多余的技术。

  这很容易理解:在只有一个 CPU 的系统中,谁还会设计多个 CGroup,哪里还能用到 NUMA domain 等概念呢?

  此外 BFS 使用单一的 run queue,不再需要复杂的负载均衡机制。由于不再有 CGROUP 概念,也不再需要 Group 间的负载均衡。

  这些简单的裁剪使得 BFS 的代码极大地简化,简化的代码意味着执行一次调度所需要的指令数减少了,相应的 footprint 自然也减少了。

  当然简化代码只是一个显而易见的方面,更重要的是,这种理念的不同会对最终的调度器实现产生更加深远的影响,这实在是难以尽述。

  多队列 vs 单一队列

  在 Linux 内核进入 2.6 时,调度器采用 per cpu run queue 从而克服了单一 run queue 的局限。在多 CPU 系统中,单一 run queue 意味着 run queue 成为了系统的瓶颈,因为在同一时刻,一个 CPU 访问 run queue 时,其他的 CPU 即使空闲也必须等待。当使用 per CPU 的 run queue 之后,每个 CPU 不必再使用大锁,从而能够并行地处理调度。

  但很多事情都不像第一眼看上去那样简单。

  Kolivas 发现,采用 per cpu run queue 所带来的好处会被追求公平性的 load balance 代码所抵消。在目前的 CFS 调度器中,每颗 CPU 只维护本地 run queue 中所有进程的公平性,为了实现跨 CPU 的调度公平性,CFS 必须定时进行 load balance,将一些进程从繁忙的 CPU 的 run queue 中移到其他空闲的 run queue 中。

  这个 load balance 的过程需要获得其他 run queue 的锁,这种操作降低了多运行队列带来的并行性。

  并且在复杂情况下,这种因 load balance 而引入的 footprint 将非常可观。

  当然,load balance 引入的加锁操作依然比全局锁的代价要低,这种代价差异随着 CPU 个数的增加而更加显著。但请您注意,BFS 并不打算为那些拥有 1024 个 CPU 的系统工作,假若系统中的 CPU 个数有限时,多 run queue 的优势便不明显了。

  而 BFS 采用单一队列之后,每一个需要调度的新进程都可以在全局范围内查找最合适的 CPU,而无需 CFS 那样等待 load balance 代码来决定,这减少了多 CPU 之间裁决的延迟,最终的结果是更小的调度延迟。

  向前看还是向后看?

  多年来 Kolivas 一直关注着 Linux 在 desktop 上的表现。对于 desktop 的用户,最注重的不是系统的吞吐量,而是交互性程序的流畅体验。从 SD 开始,Kolivas 就告诉内核黑客们,完全公平能够从根本上保证交互性。他始终坚持一个基本观点:调度器应该 forward look only。决不要去考虑一个进程的过去。

  CFS 却偏偏要考虑进程的过去。2.6.23 的时候,CFS 记录并使用 sleep time。之后不久,在 2.6.24 发布的时候,CFS 合并了“Real Fair Scheduler”,删除了 sleep time。因此在 2.6.24 之后的内核中,CFS 终于也不再考虑进程过去的睡眠时间。

  但 CFS 还是保留了 sleeper fairness 的思想,当进程 wakeup 的时候,在 place_entity() 函数中,CFS 将对 sleeper 进行奖励,以便其能尽快得到 CPU。这个策略是非常微妙的,我们在 2.1 节中详细介绍了 sleeper fairness 的演进过程。假如您花些时间回头再看看,就会发现 sleeper fairness 曾造成怎样严重的延迟问题。虽然 Ingo 自称 Gentle fairness 解决了延迟问题,但从代码上看,Gentle Fairness 只是对 sleeper 的奖励减半而已。因此我们可以说,CFS 依然对 Sleeper 进程进行奖励,这代表着一种偏好,一种“不公平”。而这,正是 BFS 所反对的。

  BFS 中,当一个进程 wakeup 时,调度器将根据进程的 deadline 来进行选择(关于 deadline 本文将在第 4 章中详细描述),其结果是,更早睡眠的进程能更快地得到调度;CFS 的 sleeper fairness 则意味着要根据 wakeup 的时间来选择下一个被调度的进程,更早 wakeup 的进程会更快得到调度。

  这种不同究竟会对桌面应用造成何种影响尚没有理论依据可以参考。但我个人认为,BFS 的策略更加合理。

  您现在可能已经读得有些烦躁了 ( 这些英文加中文的说些啥啊 ),所以我还是尽快介绍一下 BFS 的实现细节吧。然后或许您会理解我,有些词还是不翻译更好。

望能有更专业的分析和评测。

责任编辑:zhaolei 来源: IBM
相关推荐

2011-01-21 07:36:00

LinuxBFSCFS

2021-07-02 06:54:44

Linux内核主调度器

2023-12-18 09:31:35

Go容器Linux

2021-05-12 07:50:02

CFS调度器Linux

2017-03-25 21:33:33

Linux调度器

2010-01-28 10:11:13

Linux 2.6公平调度器

2020-10-13 09:23:57

LinuxKernel调度器

2021-07-05 06:51:45

Linux内核调度器

2017-02-09 09:00:14

Linux IO调度器

2019-12-02 09:45:45

Linux IO系统

2017-09-01 12:26:18

Linux调度器系统

2009-09-16 08:40:53

linux进程调度linuxlinux操作系统

2011-01-13 10:01:27

2023-11-26 18:54:29

Linux调度器

2023-11-20 22:55:00

Goroutine调度器

2021-01-29 08:22:03

调度器Yarn架构

2023-04-17 08:13:13

KubernetesPod

2011-01-06 11:36:00

linuxGCC编译器

2009-02-18 13:43:00

2022-02-15 18:45:35

Linux进程调度器
点赞
收藏

51CTO技术栈公众号