<<Linux内核设计与实现>>读书笔记(四)-进程的调度

主要内容:

  • 什么是调度
  • 调度实现原理
  • Linux上调度实现的方法
  • 调度相关的系统调用

1.什么是调度

现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好地运行,需要一个管理程序来管理计算机上同时运行的各个任务(也就是进程).

这个管理程序就是调度程序,它的功能说起来很简单:

  1. 决定哪些进程运行,哪些进程等待
  2. 决定每个进程运行多长时间

此外,为了获得更好的用户体验,运行中的进程还可以立即被其他更进行的进程打断.

总之,调度是一个平衡的过程.一方面,它要保证各个运行的进程能够最大限度的使用CPU(尽可能少地切换进程,进程切换过多,CPU的时间会浪费在切换上);另一方面,保证进程能公平的使用CPU(即防止一个进程长时间独占CPU的情况).


2.调度实现原理

前面说过,调度功能就是决定哪个进程运行以及进程运行多长时间.

决定哪个进程运行以及运行多长时间都和进程优先级有关.为了确定一个进程到底能持续多长时间,调度中还引入了时间片的概念.

2.1 关于进程的优先级

进程的优先级有两种度量方法,一种是Nice值,一种是实时优先级.

Nice值的范围是 -20~+19,值越大优先值最低,也就是说-20的进程优先级最大.

实时优先级的范围是0~99,与nice值的定义相反,实时优先级是值越大优先级越高.

实时进程都是一些对响应时间要求比较高的进程,因此系统中有实时优先级高的进程处于运行队列的话,它们会抢占一般的进程的运行时间.


进程的2种优先级会让人不好理解,到底哪个优先级更优先?一个进程同时有2种优先级怎么办?

其实linux的内核早就有了解决办法.

对于第一个问题,到底哪个优先级更优先?

答案是实时优先级高于nice值,在内核中,实时优先级的范围是0~MAX_RT_PRIO-1,MAX_TR_PRIO的定义参见include/linux/sched.h

1611 #define MAX_USER_RT_PRIO        100
1612 #define MAX_RT_PRIO             MAX_USER_RT_PRIO

nice值在内核中的范围是MAX_RT_PRIO~MAX_RT_PRIO+40 即MAX_RT_PRIO~MAX_PRIO

1611 #define MAX_USER_RT_PRIO        100
1612 #define MAX_RT_PRIO             MAX_USER_RT_PRIO

第二个问题,一个进程同时有2种优先级怎么办?

答案很简单,就是一个进程不可能有2个优先级.一个进程有了实时优先级就没有nice值,有了Nice值就没有实时优先级.

我们可以通过以下命令查看进程的实时优先级和Nice值:(其中RTPRIO是实时优先级,NI是Nice值)

$ ps -eo state,uid,pid,ppid,rtprio,ni,time,comm
S   UID   PID  PPID RTPRIO  NI     TIME COMMAND
S     0     1     0      -   0 00:00:00 systemd
S     0     2     0      -   0 00:00:00 kthreadd
S     0     3     2      -   0 00:00:00 ksoftirqd/0
S     0     6     2     99   - 00:00:00 migration/0
S     0     7     2     99   - 00:00:00 watchdog/0
S     0     8     2     99   - 00:00:00 migration/1
S     0    10     2      -   0 00:00:00 ksoftirqd/1
S     0    12     2     99   - 00:00:00 watchdog/1
S     0    13     2     99   - 00:00:00 migration/2
S     0    15     2      -   0 00:00:00 ksoftirqd/2
S     0    16     2     99   - 00:00:00 watchdog/2
S     0    17     2     99   - 00:00:00 migration/3
S     0    19     2      -   0 00:00:00 ksoftirqd/3
S     0    20     2     99   - 00:00:00 watchdog/3
S     0    21     2      - -20 00:00:00 cpuset
S     0    22     2      - -20 00:00:00 khelper

2.2 关于时间片

有了优先级,可以决定谁先运行了.但是对于调度程序来说,并不是运行一次就结束了,还必须知道时间间隔多久进行下次调度.

于是就有了时间片的概念.时间片是一个数值,表示一个进程被抢占前能持续运行的时间.

也可以认为是进程在下次调度发生前运行的时间(除非进程主动放弃CPU,或者有实时进程来抢占CPU).

时间片的大小设置并不简单,设大了,系统响应变慢(调度周期长);设小了,进程频繁切换带来的处理器消耗.默认的时间片一般是10ms.

2.3 调度实现原理(基于优先级和时间片)

下面举个直观的例子来说明:

假设系统中只有3个进程ProcessA(NI=+10),ProcessB(NI=0),ProcessC(NI=-10),NI表示进程的Nice值,时间片=10ms

1)调度前,把进程优先级按一定的权重映射成时间片(这里假设优先级高一级相当于多5msCPU时间).
  
  假设ProcessA分配了一个时间片10ms,那么ProcessB的优先级比ProcessA高10(Nice值越小优先级越高),ProcessB应该分配10*5+10=60ms,以此类推,ProcessC分配20*5+10=110ms

2)开始调度时,优先调度分配CPU时间多的进程.由于ProcessA(10ms),ProcessB(60ms),ProcessC(110ms).显然先调用ProcessC

3)10ms(一个时间片)后,再次调用时,ProcessA(10ms),ProcessB(60ms),ProcessC(110ms).ProcessC刚运行了10ms,所以编程100ms.此时仍然先调用ProcessC.

4)再调度4次后(4个时间片),ProcessA(10ms),ProcessB(60ms),ProcessC(60ms).此时ProcessC和ProcessB的CPU时间一样,这时得看ProcessB和ProcessC谁在CPU运行队列的前面,假设ProcessB在前面,则调度ProcessB

5)10ms(一个时间片后),ProcessA(10ms),ProcessB(50ms),ProcessC(60ms).再次调度ProcessC

6)ProcessB和ProcessC交替运行,直至ProcessA(10ms),ProcessB(10ms),ProcessC(10ms).
  这时得看ProcessA,ProcessB,ProcessC谁在CPU运行队列的前面就先调度谁.这里假设调度ProcessA

7)10ms(一个时间片)后,ProcessA(时间片用完后退出),ProcessB(10ms),ProcessC(10ms).

8)再过2个时间片,ProcessB和ProcessC也运行完退出.

这个例子很简单,主要是为了说明调度的原理,实际的调度算法虽然不会这么简单,但是基本的实现原理也是类似的:

1)确定每个进程能占多少CPU时间(这里确定CPU时间的算法有很多,根据不同的需求会不一样)
2)占用CPU时间多的先运行
3)运行完后,扣除运行进程的CPU时间,再回到1)

3.Linux上调度实现的方法

Linux上调度算法是不断发展的,在2.6.23内核以后,采用了”完全公平调度算法”,简称CFS.

CFS算法在分配每个进程的CPU时间时,不是分配给它们一个绝对的CPU时间,而是根据进程的优先级分配给它们一个占用CPU时间的百分比.

cfs定义了一种新的模型,它给cfs_rq(cfs的run queue)中的每一个进程安排一个虚拟时钟,vruntime。如果一个进程得以执行,随着时间的增长(也就是一个个tick的到来),其vruntime将不断增大。没有得到执行的进程vruntime不变。
而调度器总是选择vruntime跑得最慢的那个进程来执行。这就是所谓的“完全公平”。为了区别不同优先级的进程,优先级高的进程vruntime增长得慢,以至于它可能得到更多的运行机会。

3.1 CFS基本设计思路

CFS思路很简单,就是根据各个进程的权重分配运行时间(权重怎么来的后面再说)。

进程的运行时间计算公式为:

分配给进程的运行时间 = 调度周期 * 进程权重 / 所有进程权重之和 (公式1)

调度周期很好理解,就是将所有处于TASK_RUNNING态进程都调度一遍的时间,差不多相当于O(1)调度算法中运行队列和过期队列切换一次的时间(对O(1)调度算法看得不是很熟,如有错误还望各位大虾指出)。举个例子,比如只有两个进程A, B,权重分别为1和2,调度周期设为30ms,那么分配给A的CPU时间为:30ms * (1/(1+2)) = 10ms;而B的CPU时间为:30ms * (2/(1+2)) = 20ms。那么在这30ms中A将运行10ms,B将运行20ms。

公平怎么体现呢?它们的运行时间并不一样阿?

其实公平是体现在另外一个量上面,叫做virtual runtime(vruntime),它记录着进程已经运行的时间,但是并不是直接记录,而是要根据进程的权重将运行时间放大或者缩小一个比例。

我们来看下从实际运行时间到vruntime的换算公式
vruntime = 实际运行时间 * 1024 / 进程权重 。 (公式2)

为了不把大家搞晕,这里我直接写1024,实际上它等于nice为0的进程的权重,代码中是NICE_0_LOAD。也就是说,所有进程都以nice为0的进程的权重1024作为基准,计算自己的vruntime增加速度。还以上面AB两个进程为例,B的权重是A的2倍,那么B的vruntime增加速度只有A的一半。现在我们把公式2中的实际运行时间用公式1来替换,可以得到这么一个结果:
vruntime = (调度周期 * 进程权重 / 所有进程总权重) * 1024 / 进程权重 = 调度周期 * 1024 / 所有进程总权重

看出什么眉目没有?没错,虽然进程的权重不同,但是它们的 vruntime增长速度应该是一样的 ,与权重无关。好,既然所有进程的vruntime增长速度宏观上看应该是同时推进的,
那么就可以用这个vruntime来选择运行的进程,谁的vruntime值较小就说明它以前占用cpu的时间较短,受到了“不公平”对待,因此下一个运行进程就是它。这样既能公平选择进程,又能保证高优先级进程获得较多的运行时间。这就是CFS的主要思想了。

或者可以这么理解:CFS的思想就是让每个调度实体(没有组调度的情形下就是进程,以后就说进程了)的vruntime互相追赶,而每个调度实体的vruntime增加速度不同,权重越大的增加的越慢,这样就能获得更多的cpu执行时间。

再补充一下权重的来源,权重跟进程nice值之间有一一对应的关系,可以通过全局数组prio_to_weight来转换,nice值越大,权重越低。

3.2 CFS数据结构

每一个task_struct中都有一个sched_entity,进程的vruntime和权重都保存在这个结构中。那么所有的sched_entity怎么组织在一起呢?红黑树。所有的sched_entity以vruntime为key(实际上是以vruntime-min_vruntime为key,是为了防止溢出,反正结果是一样的)插入到红黑树中,同时缓存树的最左侧节点,也就是vruntime最小的节点,这样可以迅速选中vruntime最小的进程。
注意只有等待CPU的就绪态进程在这棵树上,睡眠进程和正在运行的进程都不在树上。

4.调度相关的系统调用

调度相关的系统调用主要有2类:

1)与调度策略和进程优先级相关(就是上面提到的各种参数,优先级,时间片等等)-下表中的前8个
2)与处理器相关-下表中的最后3个

系统调用描述
nice()设置进程的nice值
sched_setscheduler()设置进程的调度策略,即设置进程采取何种调度算法
sched_getscheduler()获取进程的调度算法
sched_setparam()设置进程的实时优先级
sched_getparam()获取进程的实时优先级
sched_get_priority_max()获取实时优先级的最大值,由于用户权限的问题,非roor用户并不能设置实时优先级为99
sched_get_priority_min()获取实时优先级的最小值,理由与上面类似
sched_rr_get_interval()获取进程的时间片
sched_setaffinity()设置进程的亲和力,其实就是保存在task_struct中的cpu_allowed这个掩码标志.该掩码的每一位对应一个系统可用的处理器,默认所有位都被设置,即该进程可以在系统中所有处理器上执行.
用户可以通过此函数设置不同的掩码,使得进程只能在某一个或某几个处理器上运行.
sched_getaffinity()获取进程的处理亲和力
sched_yield()暂时让出处理器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值