OS调度策略:Proportional Share(比例份额调度)

2023年 9月 16日 24.3k 0

本文是自己在深入阅读Operating Systems Three Easy Pieces关于Proportional Share(比例份额调度)的知识点总结,后面会不断更新连续每一节,欢迎大家批评指正。
也是面试经典问题总结

0. overview

💡 本节介绍一种不同的调度器→proportional-share(fair-share)比例份额调度器或者称为公平份额的调度器。其想法很简单:不是关注周期时间和响应时间,该调度器而是保证每个进程作业获得**特定比例份额的CPU时间。**

经典的调度有lottery scheduling→彩票调度。其想法很简单:获得彩票的进程应该得到运行,经常运行的进程应该有更多机会去获得彩票。

1. basic idea : random tickets(lottery scheduling)

彩票调度是一个非常基本的概念:彩票ticket,用于表示进程(或用户或其他)应接收的资源份额。

持有彩票很简单:调度程序必须知道总共有多少张彩票(在我们的示例中,有 100 张)。

调度器会随机选一个获奖的彩票(这里假设有100张,则选的号码为0-99),持有获奖彩票的进程能够得到运行。

如A有彩票0到74,B有彩票75-99。则按道理A会运行百分之75的时间。B会运行百分之25

若调度器选获奖的彩票为85,则B开始运行。

image.png

这两个工作的时间越长,它们就越有可能达到预期的百分比。

💡 优点:该方法实现简单,不需要记录全局的状态,只要记录总共有多少个ticket和每个进程的ticket数量就好了。 **其不足之处**:调度器随机选一个获奖的彩票号运行,可能不能够满足预期的程序运行的百分比。

1.1 ticket mechanisms

  • ticket currency票值
  • ticket transfer :一个进程短暂的将其ticket交给另外的程序,让其获得更高的次数
  • ticket inflation:一个进程能够短暂地提高或降低其ticket数量
  • 1.2 Implementation

    image.png

    其实现很简单:所需要的只是一个好的随机数生成器来选择获胜的彩票ticket,一个系统进程的数据结构(例如,列表)以及彩票总数。

    如此时有三个进程,A,B,C分别有100,50,250的ticket数量,总共有400个ticket。假设调度器随机生成获胜的ticket为300。

    其简单的遍历该列表,通过累加每个节点的tix数,直到累加的数大于300,则该节点就是即将运行的进程。

    为了能够是程序更加高效,我们可以将其列表排序。

    1.3 精确份额的ticket→stride scheduling

    原来随机的scheduling的其不足之处:调度器随机选一个获奖的彩票号运行,可能不能够满足预期(不精确)的程序运行的百分比。

    这里提出精确的调度方法→stride scheduling步调调度

    其思想:系统中的每个作业都有一个步幅stride,与它拥有的票数成反比。初始每个作业有个pass value,初始为0。每次运行时,选择pass value最小的值的作业运行,运行完后将其作业的pass value叠加其stride值。

    计算stride步幅:用一个较大数,除以每个作业拥有的ticket数量。

    如:A,B,C有100,50,250个ticket,则其stride分别可以计算为用10000除以其每个ticket。→100,200,40。

    image.png

    如上图,初始每个作业的pass value均为0,运行时选择最小的作业进行运行,假设选取A,则运行完后A的pass value叠加成100,此时从pass value在选最小的,假设选B。则B的pass value叠加成200,最后。。。。

    最后运行完,发现A,B,C运行的比例和原始的ticket的比例份额事准确一样的。

    💡 为什么不用该stride方法呢?而用random彩票调度的方法? **random彩票调度具有stride步幅调度没有的一个很好的属性:没有保存全局状态。** 该步幅调度,需要保存每个作业全局状态信息,如pass value和stride值。**假设此时新来一个进程作业D**,则其初始pass value设置为0,则后面D就会垄断CPU的执行了。

    所以对比之下,彩票调度可以更轻松地以合理的方式整合新的作业。

    2. The Linux Completely Fair Scheduler (CFS)

    当前linux实现了完全公平的调度,提高了效率和可伸缩性。CFS的目标是能够花尽量少的时间做出调度决策。研究表明,调度会使用CPU的5%的时间。

    因此,尽可能地减少这种开销是现代调度器架构的一个关键目标。

    2.1 CFS basic operation

    不像大多数调度器一样,将时间片化成固定的。CFS会将时间片进行变化:公平地将CPU的运行时间均匀划分给每个竞争的进程。当每个进程运行时,会累积其vruntime(虚拟运行时间),随着程序的运行,其vruntime会增加。当调度发生时CFS会选择最低的vruntime进程进行运行。

    问题1: CFS什么时候进行调度切换呢?

  • 通过参数sched-latency来决定。将sched-latency除以正在运行的进程个数,得到其每个进程运行的时间片长度,当每个进程运行完其时间片时,则会发现调度切换,CFS会选择最低的vruntime进程进行运行。
  • 例如,假设sched-latency = 48 ms,如果有 n = 4 个进程正在运行,CFS 会将sched-latency值除以 n,得出每个进程 12 毫秒的时间片。CFS 然后调度第一个作业并运行它,直到它使用了 12 毫秒的(虚拟)运行时,然后检查是否有一个具有较低 vruntime 的作业来代替运行。在这种情况下,有,CFS 将切换到其他三个工作之一,依此类推。图 9.4 显示了一个示例,其中四个作业(A、B、C、D)分别以这种方式运行两个时间段。然后完成其中两个(C,D),只剩下两个,然后每个以Round-robin方式运行24毫秒。

    image.png

    问题2: 若有太多的进程在运行,那么时间片会不会很短,从而经常发生调度切换,影响其性能?

  • 第二个参数min_granularity,决定了时间片的最短时间,当sched-latency除以正在运行的进程个数,得到其每个进程运行的时间片长度小于min_granularity时,则会将时间片设置为min_granularity。
  • 2.2 CFS控制程序的优先级→nice table?

    CFS 还支持对进程优先级的控制,使用户能够或管理员为某些进程提供更高的 CPU 运行份额。它不是通过ticket来做到这一点,而是通过一种经典的UNIX机制来实现的,这种机制被称为进程的nice level。对于进程,nice 参数可以设置为 -20 到 +19 之间的任意位置,默认值为 0。正值意味着优先级较低,负值表示较高优先级;

    CFS 将每个过程的nice值映射到一个权重

    image.png

    所以其时间片段计算改为:

    image.png

    假设有两个进程作业A,B,我们更喜欢A,则给A分配低的nice值-5,B分配为0。

    则A的权重为3121,B为1024。通过计算,分给A的时间片段是B的三倍。从而改变其份额优先级分配。

    除了改变其时间片段,其虚拟时间vruntime的叠加也发生改变:

    image.png

    A的vruntime时间的叠加比B慢三分之一,所以A运行的更久

    构建上述权重表的一个聪明方面是,当 nice 值的差异是恒定的时,该表保留了 CPU 比例比率。例如,如果进程 A 的 nice 值是 5(不是 -5),而进程 B 的 nice 值是 10(不是 0),CFS 会以与之前完全相同的方式调度它们。

    2.3 CFS using Red-Black tree

    如上所述,CFS 的一个主要关注点是效率。对于调度器来说,效率有很多方面,但其中之一就是这么简单:当调度器必须找到下一个要运行的作业时,它应该尽快完成。像列表这样的简单数据结构无法扩展:现代系统有时由数千个进程组成,因此每隔几毫秒就搜索一个长列表是一种浪费。

    所以CFS使用的是红黑树。CFS并不把所有进程都保留在这个结构中;相反,只有正在运行的(或可运行的)进程被保留在其中。如果进程进入睡眠状态(例如,等待 I/O 完成,或等待网络数据包到达),则会将其从树中删除并跟踪其他位置。

    如:假设有 10 个作业,并且它们具有以下 vruntime 值:1、5、9、10、14、18、17、21、22 和 24。如果我们将这些作业保存在有序列表中,则找到下一个要运行的作业将很简单:只需删除第一个元素即可。但是,将该作业放回列表中时(按顺序),则需要找到合适的插入位置进行插入,需要消耗O(n)的时间。但是使用红黑树只需要O(logn)的时间。提高其效率;

    2.4 CFS处理I/O等长时间睡眠的进程

    选择最低 vruntime 来运行会导致一些问题,如会出现在长时间休眠的进程作业中。想象两个进程,A 和 B,其中一个 (A) 连续运行,另一个 (B) 进入休眠,睡眠很长一段时间(比如 10 秒)。当 B 醒来时,它的 vruntime 将比 A 晚 10 秒,因此B 现在将在接下来的 10 秒内独占 CPU,同时赶上A,实际上使 A 处于饥饿状态。

    解决办法:

    当进程唤醒时,CFS 通过更改作业唤醒时的 vruntime来处理这种情况。将其vruntime设置为红黑树中最小的vruntime值。这样避免了其他进程的挨饿现象,但睡眠时间短的作业经常无法获得 CPU 的公平份额。

    3. summary

    公平共享的调度程序有其公平的问题。一个问题是,这种方法与I / O不是特别吻合;如上所述,偶尔执行 I/O 的作业可能无法获得其公平的 CPU 份额。另一个问题是,他们留下了ticket或优先级分配的难题,即,应该分配多少ticket,或者设置nice值哪个好值?

    相关文章

    服务器端口转发,带你了解服务器端口转发
    服务器开放端口,服务器开放端口的步骤
    产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
    如何使用 WinGet 下载 Microsoft Store 应用
    百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
    百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

    发布评论