我们的电脑和手机可以同时做很多事情,比如你可以一边听歌一边刷朋友圈,也可以一边刷抖音一边在微信上和朋友聊天。这些都得益于电脑和手机上的操作系统,使得多个任务可以同时运行。

说是同时运行,实际上每个时刻只能有一个任务占用CPU,确切地说是CPU内核,因为现在不管是电脑还是手机,所使用的CPU都是多核的。即使如此,每一个内核在某一个时刻只能运行一个任务。操作系统统一来调度任务对CPU的使用权,由于在不同的任务之间切换的速度(一般在毫秒级别)远远小于人类所能感知的时间,所以我们觉得这些任务是在同时运行着的。

那么操作系统是怎么来决定在某一时刻运行哪个任务呢?QNX常见的调度策略有如下两种:

  • FIFO(First In First Out):先进先出调度策略
  • Round Robin:基于时间片的轮询调度策略

在介绍这两种调度策略之前,我们要先声明调度策略是基于相同优先级任务之间进行讨论的,如果有更高优先级的任务处于Ready状态的话,它会立即抢占CPU,因为QNX是一个可抢占的操作系统。

FIFO

FIFO(First In First Out),顾名思义谁先来(谁先变为Ready状态)的就先执行谁,这有点像我们在医院排队看病,谁排在前面就先给谁看(有生命危险的病人可以插队,相当于优先级比较高的任务)。

FIFO最大的特点是,正在执行中的任务可以一直占用CPU,直到它执行结束。假如有一个任务在做一个非常长的数学运算,又没有更高优先级的任务处于Ready状态,这个任务将会一直执行直到运算完毕。如同前面的一个病人病情十分复杂,占用了医生很长时间,此时排在后面的病人只能等着。那么和它相同优先级的任务会如何呢?答案是一直处于等待状态,同理更低优先级的任务也是一直处于等待状态。

如果执行中的任务退出或者自愿放弃CPU,此时操作系统会寻找排在前面的相同优先级的Ready任务来运行,如果没有这样的任务,操作系统会继续寻找更低优先级的Ready任务来使用CPU。需要注意的是,任务自愿放弃CPU有两种情况:

  • 任务进入睡眠状态或者被信号量阻塞(需要访问的资源此时被别的任务占用)。这种情况下如果没有相同优先级的Ready任务,更低优先级的任务是可能被执行的。
  • 任务调用sched_yield()让出CPU。此时仅仅是把CPU的使用权让给具有相同优先级的Ready任务,如果没有相同优先级的任务处于Ready状态,最初的任务(调用sched_yield()的任务)将继续运行。由此可见,sched_yield()的作用是,有效率地把CPU的使用权让给其他具有相同优先级的Ready任务。

Round Robin

关于Round Robin名字的由来,有一段有趣的故事。

round robin来源于法语ruban rond(round ribbon),意思是环形丝带。

在17、18世纪时法国农民希望以请愿的方式抗议国王时,通常君主的反应是将请愿书中最前面的两至三人逮捕并处决,所以很自然地没有人希望自己的名字被列在前面。为了对付这种专制的报复,人们在请愿书底部把名字签成一个圈(如同一条环状的带子),这样就找不出带头大哥了,于是只能对所有参与者进行同样的惩罚。

Round Robin调度策略和FIFO很像,不同的是一个任务不会一直占用CPU(如果当前有相同优先级的任务)。一个任务只会运行事先定义好的固定的一段时间,这一段时间被称为时间片,时间片的值可以通过调用sched_rr_get_interval()来进行设置。时间片通常是4ms,实际上是ticksize的4倍,ticksize可以通过ClockPeriod来查询或设置。

当操作系统启动一个任务时,它会同时开始计时。当时间片设定的时间到了之后,操作系统会去查看是否有相同优先级的任务处于Ready状态。如果有,操作系统将启动该任务,否则操作系统将会继续让之前的任务运行,该任务又重新获得了一个时间片。

调度的总体规则(基于一个CPU)

  • 某个时刻只能有一个任务处于运行状态
  • 最高优先级的Ready任务将获得CPU的使用权
  • 一个任务将会运行直到它退出或者被阻塞
  • 一个Round Robin任务将会运行一个时间片的时间,结束后操作系统会再次调度它(如果需要的话)

以上对QNX的任务调度策略做了一个简单的介绍,如果你觉得有帮助请分享出去,让更多的人看到,谢谢。