实时操作系统---内核结构

 


前言

上篇文章我们主要介绍了实时操作系统的基本概念,相关内容可翻看前片博客: 实时操作系统简介

接下来我们来分析一下实时操作系统的内核结构。


一:代码临界段

首先我们先来了解下代码临界段的概念,代码临界段,也称之为代码临界区,指处理时不可分割的代码。一旦这部分代码开始执行则不允许中断打入,为了保证临界段的顺利执行,在进入临界段之前我们要关中断,在临界段代码执行完毕后我们在将中断打开。
        微处理器一般都有关中断/开中断指令,μC/OS-Ⅱ中的这两个宏调用分别是:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。因为这两个宏的定义取决于所用的微处理器,故在文件 OS_CPU.H 中可以找到相应宏定义。每种微处理器都有自己的 OS_CPU.H 文件。

二:任务      

μC/OS-Ⅱ可以管理多达 64 个任务,但目前版本的μC/OS-Ⅱ有两个任务已经被系统占用了。作者保留了优先级为 0、1、2、3、OS_LOWEST_PRIO-3、OS_LOWEST_PRI0-2,OS_LOWEST_PRI0-1 以及 OS_LOWEST_PRI0 这 8 个任务以被将来使用。OS_LOWEST_PRI0 是作为定义的常数在 OS_CFG.H 文件中用定义常数语句#define constant 定义的。因此用户可以有多达 56 个应用任务。必须给每个任务赋以不同的优先级,优先级可以从 0 到OS_LOWEST_PR10-2。优先级号越低,任务的优先级越高。μC/OS-Ⅱ总是运行进入就绪态的优先级最高的任务。目前版本的μC/OS-Ⅱ中,任务的优先级号就是任务编号(ID)。优先级号(或任务的 ID 号)也被一些内核服务函数调用,如改变优先级函数OSTaskChangePrio(),以及任务删除函OSTaskDel()。
       为了使μC/OS-Ⅱ能管理用户任务,用户必须在建立一个任务的时候,将任务的起始地址与其它参数一起传给下面两个函数中的一个:OSTastCreat 或 OSTaskCreatExt()。OSTaskCreateExt()是 OSTaskCreate()的扩展,扩展了一些附加的功能。这个后面我们再做详细介绍

三:任务状态

ucos2是按照只有一个CPU来设计的,在任何时刻都只有一个任务在占用CPU,其他的任务只能处于其他的状态。ucos2设计了5中不同的任务状态,如下所示

  • 睡眠状态:任务只是以代码的形式驻留在程序空间(ROM或RAM),还没有交给操作系统管理时的情况叫做睡眠状态。简单地说,任务在没有被配备任务控制块或被剥夺了任务控制块时的状态叫做任务的睡眠状态

  • 就绪状态:如果系统为任务配备了任务控制块且在任务就绪表中进行了就绪登记,则任务就具备了运行的充分条件,这时任务的状态叫做就绪状态

  • 运行状态:处于就绪状态的任务如果经调度器判断获得了CPU的使用权,则任务就进入运行状态,任何时刻只能有一个任务处于运行状态,就绪的任务只有当所有优先级高于本任务的任务都转为等待状态时,才能进入运行状态

  • 等待状态:正在运行的任务,需要等待一段时间或需要等待一个事件发生再运行时,该任务就会把CPU的使用权让给其他任务而使任务进入等待状态

  • 中断服务状态:一个正在运行的任务一旦响应中断申请就会中止运行而去执行中断服务程序,这时任务的状态叫做中断服务状态           

四:任务控制块                      

任务控制块是一个结构类型数据。当用户应用程序调用 OSTaskCreate()函数创建一个用户任务时,该函数就会对任务控制块中的所有成员赋予与该任务相关的数据,并驻留在RAM中。

typedef struct os_tcb {
    OS_STK *OSTCBStkPtr;    //指向任务堆栈栈顶的指针
#if OS_TASK_CREATE_EXT_EN
    void *OSTCBExtPtr;      //指向任务控制块扩展的指针
    OS_STK *OSTCBStkBottom; //指向任务堆栈栈底的指针
    INT32U OSTCBStkSize;    //任务堆栈的长度
    INT16U OSTCBOpt;        //创建任务时的选择项
    INT16U OSTCBId;         //用于存储任务的识别码。这个变量现在没有使用,留给将来扩展用 
#endif
struct os_tcb *OSTCBNext;   //指向后一个任务控制块的指针
struct os_tcb *OSTCBPrev;   //指向前一个任务控制块的指针
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN
    OS_EVENT *OSTCBEventPtr;//指向事件控制块的指针
#endif
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN
   void *OSTCBMsg;          // 指向传递给任务消息的指针
#endif
    INT16U OSTCBDly;        //任务等待的时限(节拍数)
    INT8U OSTCBStat;        //任务的当前状态标志
    INT8U OSTCBPrio;        //任务的优先级别
    //.OSTCBX, .OSTCBY, .OSTCBBitX 和 .OSTCBBitY 用于加速任务进入就绪态的过程或进入等待事件发生状态的过程
    INT8U OSTCBX; 
    INT8U OSTCBY;
    INT8U OSTCBBitX;
    INT8U OSTCBBitY;
#if OS_TASK_DEL_EN
   BOOLEAN OSTCBDelReq;     //用于表示该任务是否需要删除 
#endif
} OS_TCB;

五:任务控制块链表                    

μC/OS-在初始化时也要按照配置文件所设定的任务数事先定义一批空白任务控制块,这样当程序创建一个任务需要一个任务制块时,只要拿一个空白块填上任务的属性即可。也就是说,在任务控制块的管理上,uC/OⅡ需要两条链表:一条空任务块链表其中所有任务控制块还未分配给任务)和一条任务块链表(其中所有任务控制块已分配给任务)。具体做法为:系统在调用函数 OSInit()对C/OI系统进行初始化时,就先在RAM中建立一个OS_TCB结构类型的数组OSTCBTbl1],后把各个元素链接成一个如下图所示的链表,从而形成一个空任务块链表。从图中可以看到,C/OS-I初始化时建立的空任务链表的元素一共是Os_MAXTASKS+os_n_SYS TASKS个,其中定义在文件 OS CFG.H中的常数OS_MAXTASKS指明了用户任务的最大数目;而定义在文件UCO_II.H中的常数OS_N_SYSTASKS指明了系统任务的数目(在图中,其值为2:一个空闲任务,一个统计任务)。


        以后每当应用程序调用系统函数 OSTaskCreate()或 OSTaskCreateExt()创建一个任务时,系统就会将空任务控制块链表头指针 OSTCBFreeList指向的任务控制块分配给该任务,在给任务控制块中的各成员赋值后,就按任务控制块链表的头指针 OSTCBList将其加入到务控制块链表中。如下图所示应用程序创建了两个用户任务并使用了两个系统任务(空闲任务和统计任务)的情况时,空任务块链表和任务块链表的结构示意图。

为了加快对任务控制块的访问速度,任务控制块链表被创建为双向链表之外,μCos-在ucos_IH文件中还定义了一个数据类型为OS_TCB的数组 OSPrioTb1[]。该数组以任务的优先级别为顺序在各个元素里存放了指向各个任务控制块的指针,这样在访问一个任务的任务控制块时,就可以不必遍历任务控制块链表了。数组OSPrioTbl1[]与链表中任务控制块之间的关系下图所示。为了方便起见,人们把正在占有CPU而处在运行状态的任务所属的控制块叫做当前任务控制块。显然,当前任务控制块是μC/s-I访问频度最高的控制块,所以为了方便,μCos-i还专门定义了一个变量 OSTCBCur来存放当前任务控制块指针。uc/os-允许用户应用程序使用函数OSTaskDel()删除一个任务。删除一个任务,实质上就是把该任务的任务从任务控制块链表中删掉,并把它归还给空任务控制块链表。这样/O-I对这个没有任务控制块的任务就不再理会了,因为与这个任务对应的任务控制已被“吊销”了。由此可见,任务的任务控制块就如同人的身份证一样重要。


六:任务就绪表                  

为系统中处于就绪状态的任务分配CPU是多任务操作系统的核心工作。这项工作涉及两项技术:一是判断哪些任务处于就绪状态;二是进行任务调度。所谓任务调度,就是通过一个算法在就绪任务中确定应该马上运行的任务,操作系统用于负责这项工作的程序模块叫调度器。

系统总是从处于就绪状态的任务中来选择一个任务运行。为此,系统需要一个就绪任务登记表,它登记了系统中所有处于就绪状态的任务在C/OS-中,这个就绪表就是一个位图,系统中的每个任务都在这个位图中占据一个进制位,该位置的状态(1或0)就表示任务是否处于就绪状态。下图表示的是一个最多可以记录32个任务就绪状态的任务就绪表。实际上,它就是个类型为INT8U的数组 SRdyTb],只不过它的每一个二进制位多对应一个任务。即在就绪表中,以任务优先级别(也是任务的标识)的高低为顺序,为每个任务安排了一个二进制位,并规定该位的值为1表示对应的任务处于就绪状态,而该位的值为0则表示对应的任务处于非就绪状态。
      从图中可以看到,由于每个任务的就绪状态只占据一位,因此 OSRdyTbl1]数组的一个元素可表达8个任务的就绪状态。在本例的情况下,数组的4个元素就一共可表达32个任务的就绪状态。也就是说,每一个数组元素描述了8个任务的就绪状态,于是这8个任务就可看成一个任务组。为了便于对就绪表的查找,C/OS-I又定义了一个数据类型为INT8的变量 OSRdyGrp,并使该变量的每一个位都对应 OSRdyTb1]的一个任务组(即数组的一个元素),如果某任务组中有任务就绪,则在变量 OSRdyGrp里把该任务组所对应的位置为1;否则置为0。例如,如果OSRdyGrp=111001,那么就意味着 OSRdyTbl[o]、 OSRdyTbl[2]、OSRdyTbl[5]、 OSRdyTb[6]、 OSRdyTb[7]任务组中有任务就绪。

      由于变量 OSRdy Grp有8个二进制位,每位对应 OSRdyTbl1[]数组的一个元素,每个元素又可以记录8个任务的就绪状态,因此C/OS-I最多可以管理8×8=64个任务。如何根据任务的优先级别来找到任务在就绪表的位置呢?
      由于优先级别是一个单字节的数字,而且其最大值不会超过63,即二进制形式的00111111。因此,可以把优先级别看成是一个6位的二进制数,这样就可以用高3位(D5D4D3)来指明变量 OSRdyGrp的具体数据位,并用来确定就绪表数组元素的下标;用低3位(D2D1D0)来指明该数组元素具体数据位,
例3-5已知某一个已经就绪的任务的优先级别prio=30,试判断应该在就绪表的哪一位上置1
答:30的二进制形式为00011110,其低6位为011110,于是可知应该在 OSRdyTbl1[3]的D6(110)位上置1,同时要把变量 OSRdyGrp的D3(011)位置1

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值