前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Task之调度策略

Task之调度策略

作者头像
Taishan3721
发布2019-07-10 15:22:36
1.2K0
发布2019-07-10 15:22:36
举报
文章被收录于专栏:这里只有VxWorks这里只有VxWorks

我们知道VxWorks是一个典型的Multitasking OS(多任务操作系统),每个Task都可能有多种状态,其中处于Ready状态的Task一旦拿到CPU,就可以执行了。不过CPU的数量再多,也不太可能比Task的数量多。也就是说,总会有Task即使进入了Ready状态,也抢不到CPU,还是不能执行。

那么当有了空闲的CPU时,到底是分配给哪个Ready的Task呢?这就涉及到系统的Scheduling(调度策略)了。如果硬件是Multicore Processors,VxWorks运行的又是SMP(Symmetric MultiProcessor)模式,那情况稍微复杂些,咱们以后再聊。今天先看看UP(UniProcessor)模式的情况。 Priority 不管哪种策略,都基于一个很重要的概念,即Task的Priority(优先级)。Task的优先级是在创建时指定的,而且后期也可以动态调整,不过不建议在应用中频繁修改Task的优先级,因为可能带来了调度的不确定性。优先级的取值范围是0-255,其中0的优先级最高,255的优先级最低。具体每个Task的优先级取值是没有限制的,只是建议应用Task的优先级不小于100,驱动Task的不大于99。

Priority-based Preemptive Scheduling VxWorks传统的调度策略是基于优先级抢占,这也是默认的策略,在Vxworks Image Project中对应的component是INCLUDE_VX_TRADITIONAL_SCHEDULER

在这种策略下,如果一个Ready状态的Task的优先级比正在运行的Task的优先级高,就会发生抢占。

例如上图中,开始时只有Task C处于Ready状态,那么就是C占用CPU来执行,当Task B通过某种原因(例如taskSpawn())进入了Ready状态,就会发生抢占,即Kernel立即保存C的上下文,切换到B的上下文。只有当B通过某种原因(例如申请信号量,进入Pended状态)退出Ready状态,Kernel才能再次切换到C。 因此,永远都是Ready状态中,优先级最高的Task在执行。 Kernel为每种状态的Task都维护着一个队列,其中Ready队列又有多个List,相同优先级的Task处于同一个List。当某一优先级的Task可以执行时,都是该List最前端的Task来执行。Task在队列中的位置可能发生变化,情景如下

  • Task被其它高优先级的Task抢占后,还保持在其List的头部
  • Task退出Ready队列(例如进入Pended、Delayed、Suspended等)后,又返回Ready队列的,排在其原List的尾部
  • Task的优先级被taskPrioritySet()修改后,排在新List的尾部
  • Task的优先级被互斥信号量的继承策略临时提高后,又恢复原有优先级的,排在其原List的尾部

函数taskRotate()可以把一个Task从其List的头部移到尾部。例如taskRotate(100)就是把优先级100的List的头部的Task移到该List的尾部。如果正在执行的Task要把自己移到当前List的尾部,可以直接调用taskRotate(TASK_PRIORITY_SELF)。我们在《Task之任务的控制》中,也介绍过一种类似的方法,谁还记得?

Round-Robin Scheduling 优先级抢占策略有一个缺点,就是当多个优先级相同的Task中的某个占用CPU后,如果一直不退出Ready状态的话,其它同优先级的Task就一直得不到执行。刚刚提到的taskRotate()可以缓解这个问题,不过这会对应用开发人员有一定的要求。其实Kernel还提供了一种调度策略,时间片轮转策略(Round-robin scheduling),来解决这个问题。 时间片轮转策略也是一种传统策略,不过默认没有使能。这样策略就是给同优先级Task分配一定的时间片(Time-Slicing)来共享CPU。同一List中,每个Task执行特定的时间片后,就把CPU让给下一个Task,自己移到该List的尾部。如果时间片还没有执行完,同一List中的Task是不能抢占它的,除非它主动让出CPU或退出Ready队列。

上图中,3个Task (A、B、C)的优先级相同,它们依次执行,每次都消耗同样的时间片。如果还有其它优先级不同的Task,是不会影响Task A、B、C的时间片长度的。

上图中Task B创建了低优先级的Task X之后,X并不会影响A、B、C的时间片。因为X的优先级低一些,即使它已经在Ready队列了,也要等A、B、C全部退出Ready状态后,它才有可能占用CPU执行。

而如果有高优先级的Task进入Ready状态时,还是会立即发生抢占。上图中Task B被高优先级的Task Y抢占后,会排在同优先级List的头部。等到Y退出Ready队列后,B会继续执行之前剩余的时间片。猜一猜Kernel怎么知道B的时间片还剩余多少呢? 从上面几个例子可以看到,优先级抢占策略是始终存在的,不同优先级的Task之间一直都会遵循抢占的策略。时间片轮转策略只在同优先级的Task之间生效。而且VxWorks的这种时间片轮转默认还是关闭的。

要想打开时间片轮转策略,只需要调用函数kernelTimeSlice(),其参数为0就表示关闭该策略,参数大于零时就表示打开策略并设置时间片的长度。这个ticks指的是系统时钟的频率,因此kernelTimeSlice(sysClkRateGet()/2)表示将时间片设置为半秒。参数的数据类型使用的是int,这是因为要兼容老版本的原因,而代码中实际是按照unsigned long来操作的。 可以看到,VxWorks中所有Task的时间片都是相同的。而在有的操作系统中,优先级不同的Task会使用不同长度的时间片。例如Linux中默认的SCHED_OTHER策略,高优先级的进程/线程会占用多一些的时间片。 那VxWorks的这个时间片轮转策略在什么情况下才需要打开呢?一般是应用程序中有多个Task的优先级相同,而且它们还会同时长时间的处于Ready状态。事实上,这种场景出现的并不多。还有一点,这个策略尽量不要在运行时动态改变,不然就给Kernel的调度带来了不确定性,这可不是实时系统希望看到的。如果必须要设置时间片的话,最好放在系统启动之后,应用启动之前。

Task Locks VxWorks在UP模式里还提供了一种特殊的机制,taskLock()/taskUnlock(),可以让当前Task不被任何其它Task抢占。一个Task在调用taskLock()之后,调用taskUnlock()之前,只要它还是Ready状态,优先级再高的Task也无法抢占它;如果打开了时间片轮转,那么它的时间片计数也不会增加。这种状态只有调用了taskUnlock()才会取消。如果在taskLock()和taskUnlock()中间,这个Task阻塞或挂起了,那Kernel就恢复原有的调度策略,一旦这个Task返回Ready队列,就会再次禁止对它的抢占。而且taskLock()/taskUnlock()还是可以嵌套使用的,不过Unlock的次数要一致。

可以看到,调用taskLock()之后,禁止了所有Task(包含与当前Task没有关联的高优先级的Task)的抢占,这就影响了系统的实时响应。因此这种机制尽量少用,而且taskLock()和taskUnlock()之间的代码要尽量的简短,不要有耗时的操作。迫不得已的时候,还可以考虑使用互斥信号量来代替。 还有一点,taskLock()不能阻止Interrupt的抢占。必要的话,可以加上intLock()/intUnlock()的组合,把Interrupt的抢占也禁掉。

Other Scheduling 除了默认的优先级抢占和时间片轮转,VxWorks系统还为RTP里的POSIX thread提供了POSIX threads scheduling,INCLUDE_POSIX_PTHREAD_SCHEDULER。咱们在介绍RTP时,再介绍这种策略。 另外,VxWorks还提供了用户自定义框架,INCLUDE_CUSTOM_SCHEDULER。我们可以添加自己的调度策略,有兴趣的童鞋可以去研究一下。 这正是: VX系统实时好,级别高者优先跑。 若有同级来竞争,可以分片来运行。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 这里只有VxWorks 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档