Discuz! Board

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 46588|回复: 1

UIP-Kernel RTOS 内核解析(二)(原创)

[复制链接]

2

主题

15

帖子

64

积分

超级版主

Rank: 8Rank: 8

积分
64
发表于 2018-3-8 23:50:03 | 显示全部楼层 |阅读模式
本帖最后由 uipkernel 于 2018-3-11 17:18 编辑

       UIP-Kernel RTOS支持的多执行体编程模型(原创,转载或引用请注明出处)       作者:王保进(uipkernel)


       在RTOS中,执行体包括:任务(Task)与中断处理程序(ISR),有些RTOS将任务称作线程(Thread);严格来讲,执行体还应包括RTOS代码本身;因为,这些任务应用层代码、中断处理程序、RTOS代码都是要被CPU执行的。CPU就像一个跑步的runner,在这些代码构成的不同道路中移动并往复切换。其中有些切换是主动进行的,即,通过任务中调用的UIP-Kernel的API实现主动的任务切换;有些切换是被动进行的,即,通过CPU硬件中断机制触发的中断而实施的。无论是主动切换还是被动切换,中断处理程序切换到其它中断处理程序或者任务期间,或者任务切换到其它任务期间,CPU都会在RTOS的代码构成的“道路”上“奔跑”一会儿。

      多执行体编程模型中的“多”字,含义是,支持多个执行体的并发执行,并发是虚拟并行的意思,即,在每一时刻,CPU(单核)只能执行着某一特定程序,但从大的时间片段来看,因为CPU的来回往复切换,就好像是有多个执行体在“并行”执行。

      可以将任务模型看做一个“容器(Container)”,C语言实现时是一个执行体函数,其中承载着满足应用层功能性需求的,由开发人员编写的应用层代码。一般RTOS支持的任务执行体函数中会包含一个while(1)或for(;;)循环,应用层代码包裹于其中,被往复循环执行;但这不是必须的,像UIP-Kernel就允许任务执行体函数中没有上述循环结构,每次任务被调度执行后都可以执行到底(期间可能会被中断所打断)。那么,开发人员编写的应用层代码(或者说该任务模型)是如何与RTOS打交道(交互)的呢?是通过UIP-Kernel提供的API函数(我们称之为系统API)。
      因此,大家会发现,每个任务执行体函数中都会至少调用一个可以进入RTOS调度内核的系统API,即使上面阐述的UIP-Kernel支持的不包含while(1)的执行体函数中,最后也要调用一个能进入UIP-Kernel内核的API(TerminateTask)。那么,一个基于RTOS编写的应用程序中能否包含一个没有调用系统API的任务呢?可以,但只能有一个,就是那个优先级最低的空闲任务(当然,如果相同优先级任务采取时间片轮转的方式调度运行,则可以有多个最低优先级的空闲任务,但这样做的意义不大)。若有一个任务优先级比该空闲任务高,且没有调用系统API,则前述的空闲任务就没有被调度执行的机会了。进一步的,若某一个较高优先级的任务没有调用任一系统API,则比它优先级低的任务就再也没有机会被调度执行了。大家可以思考一下为什么

     中断是微处理器硬件(核心是CPU)为了处理现实世界中可能会随时、异步发生的事件而设计的,这些事件既包含各种状态变化引起的,也包含时间的流逝而引起的。其实,微处理器是没有真正的“中断”逻辑的,其内部是通过每个clock不断“轮询”管脚或者内部逻辑电平的变化而判断是否发生了中断,再通过硬件逻辑执行一系列确定的与现场保护有关的操作,最终将保存在指定位置(寄存器)中的数值(即,中断处理程序的地址,由编译器产生,但在系统初始化时,由初始化代码写入寄存器中)导入PC(程序计数器)寄存器中,从而切换到中断处理程序来执行。
     中断在RTOS中发挥着重要作用,不但大量执行体间的切换是由中断引起的,RTOS所标榜的保障系统实时性的抢占式调度也是由中断来保证的。所谓基于优先级的抢占式调度,就是:当有某个事件发生时,该事件关联的中断会被触发(由设备电路板的硬件设计来保证,例如:将外部环境的状态变化转换为电信号的电平变化,而该电信号是连接到微处理器的I/O管脚上的,从而触发CPU的中断),进而引起对应中断处理程序的执行(由CPU的硬件逻辑与系统软件共同保证),该中断处理程序可能会使得某相关任务就绪,因而要快速进入调度内核,若相关任务的优先级设置的较高,则通过调度内核会切换到该任务执行体函数,从而能尽快处理上述发生的事件。这就是基于优先级的抢占式调度的过程。

      可见,若要实现抢占,则中断处理程序必须存在能进入调度内核的入口,那么该入口在哪里呢?就是每个中断处理程序最后调用的系统API,而且该API是特定的,在UIP-Kernel中就是:uipIRQIntContextRestore;同时,为了实现可靠的执行体间切换,在ISR的开始位置还要调用与现场保护、系统全局变量修改等有关的系统API,在UIP-Kernel中就是:uipIRQIntContextSave
      是不是该API一定要被开发人员可见(即,由开发人员添加到其编写的ISR程序中),回答是:不一定。既然每个ISR的最后都要添加该API,则RTOS可以构建一个ISR处理框架,在开头与末尾执行了上述操作,在中间位置调用用户编写的ISR,该ISR中就无需调用上述的系统API了,简化了用户编程复杂度,避免了用户遗忘调用上述API而对系统执行特征、稳定性、实时性造成的影响。为了方便编程,会用链表保存用户的ISR指针及其它信息,且向用户提供向该链表注册、注销ISR的系统API。每次发生中断时,框架都会被触发执行,中间位置代码会遍历上述链表,根据此次中断ID号搜索到对应的ISR来调用执行。
      UIP-Kernel支持上述两种方式的中断处理程序编写模型。

      是否每个ISR都必须要调用进入调度内核的系统API呢?回答是:不一定。RTOS支持不进入调度内核的ISR,但不宜过多,且执行时间不宜过长。因为,这些ISR的执行逻辑是不受RTOS调度内核控制的,对系统软件在研制过程中进行实时性、可调度性分析时会产生一定影响。需要谨慎处理。所以,不是万不得已,尽量不要采用该处理方式;即使采用了,也要小心处理,确保系统的可预测性、可分析性与稳定可靠性。

      还要补充的是:一般ISR的处理逻辑不宜过长,尽量只保留能快速执行完的代码,例如:数据获取,状态判断等,将后续需要较长时间运行的代码放在任务中执行,哪怕是将任务设置为较高的优先级。即,遵循的原则是:将需要实时处理,能快速执行的代码放在ISR中。因为,微处理器的中断处理硬件逻辑是基于优先级的嵌套执行方式,若某个中断的优先级较高,而其处理程序的代码较长,且其中包含不是那么重要的代码,则会延迟放在低优先级中断处理程序中的重要代码的及时执行。

      可见,任务执行体函数以及中断处理程序中都会调用系统API,并因此而进入调度内核。因此,从UIP-Kernel所提供demo程序的任务函数或ISR中,借助于系统API而进入内核、分析内核也是分析UIP-Kernel的一条途径。推而广之,这也是分析系统软件、协议栈软件的一个重要方法。

       横看成岭侧成峰,分析系统软件时一定要从不同角度、不同层面进行。后面我们会从不同视角继续分析RTOS的实现技术与开发方法。


回复

使用道具 举报

2

主题

15

帖子

64

积分

超级版主

Rank: 8Rank: 8

积分
64
 楼主| 发表于 2018-3-9 22:11:47 | 显示全部楼层

更正:
      上面文字中大量使用了“中断”一词,准确的说:有些地方应该使用“异常(exception)”一词更为科学。异常包括一些系统异常,以及中断;而此处的中断才是用户用于实现某一应用功能的机制。
      例如:Cortex-M3处理器支持多种类型的异常:
               (第一类)RESET, NMI, HardWare Fault;
               (第二类)PSV,SVC等可编程中断;
               (第三类)其他的可编程中断,例如timer,GPIO等。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|UIP Kernel 论坛  

GMT+8, 2023-5-29 10:01 , Processed in 0.043506 second(s), 4 queries , File On.

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表