大家知道,CPU运行的基本单位其实是一条一条的指令,如今我们通过编译器就可以将代码生成为机器指令,也就是所谓的二进制文件,这些指令组成了程序。程序在装入内存中执行时被称之为任务,或者说是进程。随着处理器性能的日益强大,程序也越来越复杂,因此诞生了操作系统来帮助我们管理进程,更合理地分配处理器资源,这也就是任务调度的目标。而在VxWorks中,能够调度起这些任务,最核心的就是reschedule调度算法了。

不过在介绍它之前,还是要先了解下实时系统分时系的概念。我们现在接触最多的操作系统,像Windows、Linux这些都是分时操作系统,也就是把允许每个程序运行的时间划分为很短的时间片,按时间片轮流在处理器上运行,但是切换的速度非常快,这样微观上看是各程序轮流运行,宏观上看是进程们在并行工作。

而VxWorks属于实时系统,这也就要求做到及时响应和高可靠性,例如我们更期望一个系统能够始终以50微秒执行一个函数,而不期望系统平均以10微秒执行该函数,但偶尔会以75微秒执行它。这通常需要基于优先级的抢占调度,任务都被指定了优先级,在能够执行的任务(没有被挂起或正在等待资源)中,优先级最高的任务被分配CPU资源。这两种调度方式可以结合使用。

VxWorks默认采用基于优先级的抢占式调度(Priority-based preemptive scheduling)算法,同时也允许选用轮转(Round-robin)调度算法。

Wind内核有256个优先级,编号是0-255,0的优先级最高,255最低。有趣的是,这个优先级机制也并非完美。NASA的火星探路者号火星车就用的是VxWorks,在着陆后的第10天,它就开始无规律地重启,每次重启都造成了数据丢失。原因是当一个高优先级任务和低优先级任务共同需要某个资源时,就可能会导致优先级反转问题。

气象任务(低优先级)获得互斥量并写总线的时候,一个中断的发生导致了通信任务(中优先级)被调度并就绪,调度的时机正好是总线管理任务(高优先级)等待在总线访问互斥量上的时候。这种情况下,因为通信任务比气象任务优先级高,所以会抢占气象任务,当然,这也就更让总线管理任务失去了运行的机会。通信任务运行时间稍长,总线管理任务就会等待互斥量超时,返回错误,提示总线任务没有能够在一定的时间内完成总线操作,在探路者中,这种情况被当作严重错误处理,作为错误处理的结果就是整个系统被重启。

据报道,工程师们后来承认,在飞行前测试阶段就发生过一两起这样的重启,但是相当不易重现所以也没有得到解释,于是出于一种很自然的回避情绪认为这个问题不重要,并且也找到了一个托词——可能是硬件上的小毛病引起的。实际上为了防止这种高优先级任务反而被低优先级卡住的问题,VxWorks提供了优先级继承的手段,让进入临界区的任务临时继承临界区任务中的最高优先级,这时低优先级任务暂时获得了高优先级任务的优先级,系统优先级反转的情况就消失了。

内核调度器的目标是保证优先级最高的就绪任务处于运行状态,任务控制块(TCB)保存了任务的状态,下面几种是基本的。

当一个高优先级的任务由于资源释放变为就绪态,在reschedule调度后,它会立即抢占当前正在运行的较低优先级的任务。

就绪态(Ready):任务只等待系统分配CPU资源

挂起态(Pend):任务需等待某些目前不可利用的资源而被阻塞

休眠态(Sleep):如果某一个任务不需要工作,则这个任务处于休眠状态

延迟态(Delay):任务被延迟时所处的状态

根据任务的状态和类型,会把它们按顺序放到链表队列中,优先级高的任务排在队列前面,相同优先级的任务按照进入队列的先后顺序排列。这样调度的时候直接从队列头中取就好了。

readyQHead:存放就绪的任务,被抢占的任务排在同优先级任务的最前面队列头

tickQHead:存放由于时间而被延迟和休眠的任务队列头

pendQHead:存放由于资源而被挂起的任务队列头

activeQHead:活动队列包括了内核中创建的所有任务,也就包括了上面3种队列中的任务

workQ队列:环形的内核队列,存放内核产生的一些需要进行延时处理的操作

在资源释放后,任务的状态也会相应地改变。因此,进入重新调度的时机主要就是中断处理的执行退出、系统时钟更新(定时器中断)、对任务的操作(taskSpawn/Delete/Delay等)、资源的变化(如信号量semGive/Take、消息队列、内存空间)等。在这些操作触发进入reschedule后,它又干了哪些事呢?

/********************************************************************************* reschedule - portable version of the scheduler** This routine determines the appropriate task to execute, then calls any* switch and swap hooks, then loads its context.  The complicating factor* is that the kernel work queue must be checked before leaving.  In the* portable version the checking of the work queue and the loading of the* task's context is done at interrupt lock out level to avoid races with* ISRs.  An optimized version ought to load as much of the context as is* possible before locking interrupts and checking the work queue.  This will* reduce interrupt latency considerably.** The _WRS_FUNC_NORETURN attribute is applied to reschedule() so that the * compiler can dispense with generating the usual preamble that is only* required for functions that actually return.  The reschedule() function * never returns since windLoadContext() is always used to effect a change in* execution context.  Likewise the windLoadContext() routine is marked as * _WRS_FUNC_NORETURN so that the compiler can dispense with saving any * volatile registers before invoking windLoadContext().** \NOMANUAL*/_WRS_FUNC_NORETURN void reschedule (void){    FAST int  ix;       /* loop index */  pPrevTcb    = _WRS_KERNEL_CPU_GLOBAL_GET (currentCpuIndex, taskIdCurrent);    pCurrentTcb = pPrevTcb;    if (!_WRS_KERNEL_CPU_GLOBAL_GET (currentCpuIndex, workQIsEmpty))  {unlucky:  workQDoWork ();  }

从代码中可以看到,在对称多处理器核心的情况下,首先它会获取当前核心的上一次重新调度后执行的任务TCB,然后会检查当前内核workQ队列是否有还未执行的任务,如果没有的话就可以开始调度任务了。pCurrentTcb即指示当前正在运行的任务。

 pCandidateTcb = READY_Q_TASK_GET (currentCpuIndex, &nextCpu);    if (pCandidateTcb != pCurrentTcb)

从readyQ中取出当前核心的待执行任务,同时将下一个需要调度的核心号放到nextCpu。然后判断当前运行的任务是否就是要执行的任务,如果不是的话就开始了切换流程。​​​​​​​

{  pCandidateTcb->windSmpInfo.cpuIndex = currentCpuIndex;

设置将要执行任务的处理器信息为当前核心。​​​​​​

mask = pCandidateTcb->swapInMask | pCurrentTcb->swapOutMask;  for (ix = 0; mask != 0; ix++, mask = mask >> 1)    if (mask & 1)    (* taskSwapTable[ix]) (pCurrentTcb, pCandidateTcb);

然后进行任务的swap交换操作。此Hook提供给内核使用。

for (ix = 0;    (ix < VX_MAX_TASK_SWITCH_RTNS) && (taskSwitchTable[ix] != NULL);    ++ix)    (* taskSwitchTable[ix]) (pCurrentTcb, pCandidateTcb);

进行任务的switch切换操作。具体Hook函数由用户任务自己指定。这是为了让用户在切换过程中能够执行一些自定义的逻辑,而无需修改内核代码。

pCurrentTcb->windSmpInfo.cpuIndex = NONE;  _WRS_KERNEL_CPU_GLOBAL_SET (currentCpuIndex, taskIdCurrent, pCandidateTcb);  pCurrentTcb->windSmpInfo.stateRequest &= ~WIND_STATE_CHANGE;  pCurrentTcb = pCandidateTcb;  }

设置一些全局变量和状态标志,并确保pCurrentTcb始终指向切换后的任务。如果不切换的话那当前核自然不需要再进行上面的调度,这时nextCpu就派上用场了。​​​​​​​

 if (nextCpu >= 0)  {  CPUSET_ZERO (cpuSet);  CPUSET_SET  (cpuSet, nextCpu);     vxIpiEmit (IPI_INTR_ID_SCHED, cpuSet);  nextCpu = SCHED_REQ_DONE;  } 

在多核的环境下,为了维持时间的统一,减小不必要的开支,硬件定时器的中断往往只会由最先运行的主核进行响应,也就是说,其他核上其实是没有时间观念的,那么这些核上的任务在延时之后该怎么切换回来呢?这时就需要主核根据下一个CPU,向对应的核心发送核间中断,其他核心收到中断后,就会引起那个核的reschedule过程,这样每个核心就可以自己调度核上的任务了。​​​​​​​

 oldLevel = INT_CPU_LOCK ();    if (!_WRS_KERNEL_CPU_GLOBAL_GET (currentCpuIndex, workQIsEmpty))  {  INT_CPU_UNLOCK (oldLevel);    goto unlucky;      /* take it from the top... */  }

最后一次检查workQ工作队列。为什么在reschedule()的末尾还要再次判断内核队列是否为空,如果非空重新执行调度器呢?这是因为,在执行内核切换和交换钩子函数期间,有可能会有更高优先级的任务刚好需要被唤醒,由于此时Wind内核正在被reschedule调度器互斥访问(即Wind内核处于内核态,kernelState=TRUE),将这些任务转为就绪态的工作都会被放到内核队列延迟运行。执行内核队列中的任务会使得更高优先级的任务可以进入就绪态,需要重新提供一次调度的机会,这样确保了只有优先级最高的任务占据CPU。

例如,某个高优先级任务被信号量semTake住。由于该任务处在pend队列中,刚才的调度选择的是ready队列中的某个中优先级任务。而就在同一时间,定时器中断触发,该信号量被释放,在semGive中,它先会判断当前处在中断上下文或内核锁不可用,信号量将被延迟释放,在semGiveDefer中将该类型信号量的Give方法加入到了workQ队列中。这时重新执行该workQ任务,就能让这个高优先级任务重回ready队列,赶上这次的调度。​​​​​​​

 _WRS_KERNEL_CPU_GLOBAL_SET (currentCpuIndex, reschedMode,        WIND_NO_RESCHEDULE); 

从这里开始,新任务已经被确定,再也没有什么能阻止任务上下文的加载了。顺便清除下CPU的重新调度标志。​​​​​​​

KERNEL_LOCK_GIVE ();    windLoadContext ();} 

现在,加载新的任务上下文并开始执行它。

为了能更直观地了解这一过程,我们可以在SkyEye软件中对该函数过程进行一步一步地调试,这样就能真正理解每一步的作用。由迪捷软件自主开发的SkyEye全数字实时仿真软件(点击查看详情),是基于可视化建模的硬件行为级仿真平台。利用拖拽的方式快速搭建任意的虚拟硬件平台,保证虚拟嵌入式系统的可靠性和实时性,进行嵌入式软件的开发和调试。对标产品为美国风河公司的Simics。

SkyEye支持ARM、PowerPC、MIPS、DSP、SPARC等多种架构的处理器,以及定时器、中断控制器、串口、存储器、显示屏等多种硬件外设,能够运行Linux、VxWorks、FreeRTOS、RTEMS、ReWorks、SylixOS等多种嵌入式操作系统。它能够进行源代码和汇编指令级的调试,支持函数调用栈、多处理器、寄存器、变量、内存等的调试,是你在嵌入式学习道路上或业务中提高生产力的不二选择。

VxWorks任务调度相关推荐

  1. vxworks任务通信机制

    VxWorks支持各种任务间通信机制,提供了多样的任务间通信方式,主要有如下几种: Ÿ         共享内存,主要是数据的共享: Ÿ         信号量,用于基本的互斥和任务同步: Ÿ     ...

  2. 什么是真正的实时操作系统

    做嵌入式系统开发有一段时间了,做过用于手机平台的嵌入式Linux,也接触过用于交换机.媒体网关平台的VxWorks,实际应用后回过头来看理论,才发现自己理解的肤浅,也发现CSDN上好多同学们都对实时. ...

  3. 基于嵌入式操作系统VxWorks的多任务并发程序设计(3)――任务调度

    基于嵌入式操作系统VxWorks的多任务并发程序设计(3) ――任务调度 作者:宋宝华  e-mail:[email]21cnbao@21cn.com[/email] 出处:软件报 VxWorks支持 ...

  4. VxWorks的任务与任务调度

    VxWorks任务: 作为实时操作系统,任务调度是基于优先级的,且可抢占式的调度方式.同时对于相同优先级的任务,支持Round-Robin循环调度方式(以下简称RR调度) Vxworks内核三个队列: ...

  5. Vxworks、QNX、Xenomai、Intime、Sylixos、Ucos等实时操作系统的性能特点

    Vxworks.QNX.Xenomai.Intime.Sylixos.Ucos等实时操作系统的性能特点 VxWorks操作系统 VxWorks 操作系统是美国WindRiver公司于1983年设计开发 ...

  6. vxworks linux 多线程api,vxWorks多任务编程初探(转)

    进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位.程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体.而进程则 ...

  7. VxWorks中信号量实现任务间通信与同步机制分析

    引 言 多任务内核.任务调度机制.任务间通信和中断处理机制,这些都是VxWorks运行环境的核心.多任务处理和任务间通信是实时操作系统的基石.一个多任务环境允许将一个实时应用构造成一套独立任务的集合, ...

  8. vmware上安装vxworks(xp环境)

    搞了两天,终于ok了,哈皮! 我是按这篇文章一步一步去做的,但是还是有很多让你痛不欲生的错误出现,可能与电脑环境相当大的关系吧.由于网上各种解决方案我都用了,最后成功了都不知道到底哪几种才是我问题的解 ...

  9. vxWorks多任务编程初探-上

    进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位.程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体.而进程则 ...

最新文章

  1. python对于办公有什么帮助-日常工作中python能够有哪些帮助?
  2. php类库下载下来怎么使用,PHP如何实现$this-[类库名称]-[类库方法]。
  3. session 学习
  4. 华为epg-sms 多cp_Linux桌面上的SMS,Linux 25岁生日以及更多开源新闻
  5. linux下异步IO的简单例子
  6. distinct group by一起用_用ggplot2来画带有对角线的热图。
  7. 【java虚拟机系列】JVM类加载器与ClassNotFoundException和NoClassDefFoundError
  8. MySQL中的常用函数和聚合函数
  9. python中装饰器的用法_总结Python中装饰器的使用介绍
  10. 浩辰3D设计软件中如何进行弹簧设计?
  11. java重载静态方法_在Java中可以重载或覆盖静态方法吗
  12. ArcGIS中使用模型构建器快速计算图斑面积占比
  13. ipad 的android模拟器,苹果IPAD模拟器(iPadian)
  14. onenote打开闪退平板_轻松解决Win10 OneNote打不开或闪退的问题
  15. 安装打印机提示未能添加服务器,打印机未能链接到服务器
  16. 硬件工程师为什么远不如软件工程师?
  17. 【2023秋招】10月8日美团校招两道题
  18. 64位win7下Android SDK Manager闪退的解决方法
  19. 如何通过cmd怎么打开计算机管理
  20. SAP可配置(VC)的特性相关性简介

热门文章

  1. Vue组件化之VueComponent介绍
  2. springboot整合redis实现分布式锁思想
  3. MySQL-InnoDB-MVCC多版本并发控制 剖析
  4. PostgreSQL 12系统表(1)pg_class
  5. 三维立体动画制作_三维立体动画制作相比传统方式的特点
  6. php网页正文提取,通用网页正文抓取工具_任意网页正文提取API
  7. mysql面试常用命令_面试之MySQL基本命令
  8. html的模板引入,rel=import 未来的HTML模板引入方式
  9. wps怎么投递简历发到boss直聘_BOSS直聘角逐招聘季:装机量、下载增量、增长率三料冠军...
  10. java读取打包时间_Java获取响应的日期时间,这样写是否合理?