登录站点

用户名

密码

对ucos-ii内核结构的心得

4已有 1145 次阅读  2010-04-25 17:44   标签内核  心得  结构 

ucos-ii是一个多任务的操作系统,其最大优点即为实时性。任务通常是一个无限的循环,其中包括了用户代码,而实时性即指最快的响应优先级最高的任务。

确实,对于一个初步接触ucos-ii的新手来讲,想要完全理解是要花时间的。

我们想用ucos-ii,在移植成功后(以后在写移植方面吧),我们首先就得进行初始化,ucos是用函数OSInit()来完成的。

     调用OSInit()的目的是建立一些数据结构,比如初始化OS_MAX_TASK数目的OSTCBFreelist,以及用户定义的各项OSEeventFreelistOSQFreelistOSFlagFreelistOSMemFreelist(将OS_MAX_???设置为实际需求数目可减小内核RAM量,以后在写篇移植方面的文章详谈吧)。以OSTCBFreelist 为例,这可看作一条缓存链表。一开始,列表中指针全部指向NULL,当任务建立时,就从OSTCBFreelist 上取出一块赋予给任务控制块。

显然,这样做的好处在于:缓冲池更快的将事先已初始化的内存空间有序的赋予给任务。这样,我们用OSTaskCreate()OSTaskCreateExt() 建立任务时,这两个函数会调用底层的OS_TCBInit(),就从OSTCBFreelist中分配了一个任务控制块OS_TCB

初始化ucos-ii后,我们就该建立任务了。在启动OSStart()之前,一定要建立一个应用任务。假设我们已建立了若干个任务,就可以启动OSStart()了。

OSStart()的作用就是找一个优先级最高的任务的TCB,然后运行。这就有了两个疑惑:

1.       怎样找到优先级最高的任务;

2.       任务控制块TCB有些什么东西。

我们先来解决第一个问题吧。

首先,任务一被建立,它们就会被赋予相应的堆栈,相应的优先级prio等。然后就进入就绪态等待运行。内核根据优先级作了一个就续表。内核只需找出进入就绪表中优先级最高的任务进行运行即可。

非常奇妙的事就是作者构思了一套机理,一方面可由任务优先级推知其在就绪表中的位置,另一方面可由就绪表中的置位推知表中的最高优先级任务。

其原理是这样的:

   作者创建了一个8位变量OSRdyGrp 和一个8位变量数组OSRdyTbl[8]; 那么要使一个优先级为prio的任务进入就续表就需要在相应位置置1.

OSRdyGrp  |=  OSMapTbl[prio >> 3];

OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];

   当然,一开始很难让人接受,我们先来讲讲prio。由于prio最高为63,所以其8Bit中高两位一定为0. 这样的话,prio>>3就为00000YYY,而Y就代表了就绪表中的行数。不过这需要一层转换,即通过OSMapTbl[].(读者可将其看作调制器理解)。

OSMapTbl[0]=00000001

OSMapTbl[1]=00000010

OSMapTbl[2]=00000100

………….

OSMapTbl[7]=10000000

Y00YYY000,将通过OSMapTbl[]置位OSRdyGrp;同样,prio & 0x07代表了X(00000XXX),将同样置位OSRdyTbl[prio>>3]了。

(读者可能已经看出了,切记这里的置一是置8Bit中的1Bit.)

我来举个例子吧,如将prio8的任务在就绪表中置位。

 8 转换成二进制为 00(001)(000) ,则Y = prio>>3=1,那么可查OSMapTbl[]表得出OSMapTbl[prio>>3] = 00000010;所以它通过OSRdyGrp |= OSMapTbl[prio>>3];OSRdyGrp (_ _ _ _ _ _ A _) A位置1,即相当于通知就绪表,在就续表的第二行会有任务就绪;

那么 X= prio & 0x07 = 0,则OSMapTbl[prio & 0x07] = 00000001;所以它通过OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];OSRdyTbl[1](_ _ _ _ _ _ _ B)中的B位置1

那么又怎样从就绪表中找出最高优先级的任务呢?

理解了上面所述的置位,那么这个问题也就大致相同了:我们通过借助OSMapTbl[]来编码使其进入就绪态,这儿又将借助OSunMapTbl[](读者可看作解调器)来解码,使系统找出优先级最高的任务。

找出所有就绪表中优先级最高的任务:

Y = OSUnMapTbl[ OSRdyGrp ];

X = OSUnMapTbl[ OSRdyTbl[Y] ];

那么prio =Y *8 + X

OSUnMapTbl[]表如下:

INT8U  const  OSUnMapTbl[256] = {

    0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x00 to 0x0F                             */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x10 to 0x1F                             */

    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x20 to 0x2F                             */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x30 to 0x3F                             */

    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x40 to 0x4F                             */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x50 to 0x5F                             */

    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x60 to 0x6F                             */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x70 to 0x7F                             */

    7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x80 to 0x8F                             */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x90 to 0x9F                             */

    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xA0 to 0xAF                             */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xB0 to 0xBF                             */

    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xC0 to 0xCF                             */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xD0 to 0xDF                             */

    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xE0 to 0xEF                             */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0        /* 0xF0 to 0xFF                             */

};

      在举一个例子来理解吧。比如同时有 6 7 8 9 四个任务就绪。

那么OSRdyGrp = 00000011; 找出最高优先级任务所在行,Y = OSUnMapTbl[ OSRdyGrp ]; 查表会得到0,即Y=0,这里表明优先级最高的任务在就绪表中的第一行。然后再找出其在第一行的位置OSRdyTbl[Y] = OSRdyTbl[0] =11000000;

通过查表可得 X = OSUnMapTbl[ OSRdyTbl[0] = 6

[prio = Y *8 + X = 6 即就续表中优先级最高的任务为优先级为6的任务。

       想想一般的从头到尾的查找,再想想这种方法,真的很值得欣赏。

       再回头看看第二个问题吧,来讲讲OS_TCB.

       可以这样理解:TCB就是任务的信息,以确保任务在切换时无误差地还原执行任务。TCB这个结构体其下有指向当前任务堆栈栈顶的指针OSTCBStkptr,双向链表指针OSTCBNextOSTCBPrev,优先级OSTCBPrio等。那么系统找到最高优先级的任务后是怎样通过TCB进行任务切换的呢?

       这里还应介绍两个指针,OSTCBCurOSTCBHighRdy。我们通过任务优先级的值,查找任务控制块优先级表OSTCBPrioTbl[],得到指向相应任务控制块OS_TCB,用OSTCBHighRdy得到了即将执行任务的TCB,然后进行任务切换。

       我们来看看系统是怎样保存当前CPU值以及重新装入任务。首先,我们来了解一下OS_TASK_SW()这个函数。这其实就是一个软中断或者陷阱中断,执行切换任务时,中断就将依次保存PSWPC的当前值,通用寄存器的值,最后将被挂任务的堆栈指针保存在OS_TCB中,这时有OSTCBCur->OSTCBStkPtr指向了SP。当遇到要切换的任务时,将OSTCBCur指向了OSTCBHighRdy(即将执行的任务),由于保存了CPU的值OSTCBCur->OSTCBStkPtr此时指向的就是该任务的SP了。这样,重新装入只需从反方向弹出寄存器值,然后通过中断返回指令把PCPSW装回到CPU中,任务便重新运行了。

       那么任务的调度就应该是这样的:任务以各种方式(得到某资源或延时到达等)进入就绪态,系统根据就续表找到了优先级最高的任务,然后将被挂起于将执行任务的TCB分别保存与载入,达到了任务的切换。

       任务切换函数其实就是保存被挂起任务的寄存器值,把被挂起任务的SP保存在被挂起任务TCBOSTCBStkPtr。然后将TCBCur指向任务栈,依次弹出将执行任务的寄存器值,然后中断返回。

        ucos-ii任务切换是模拟的中断,当然也存在用户自定义的中断函数,时钟节拍就是中断。只不过ucos-ii中的中断在退出中断时还需判断是否有优先级更高的任务就绪,如时钟节拍,就像脉搏一样,利用定时的中断来标志或给邮箱等等发消息,以通知延时任务就绪。

     PS : ucos-ii的内核而言,还可以写很深,由于时间问题,不在多述了。第一次写博文,感觉很累啊。第一次我坚持写完这篇文章,其实应该分好几篇来讲诉,这样的话,我也可以轻松点,也可以将问题讲述得更彻底点。

                             由于本人水平有限,文中如有不当之处,请不吝指出。

上一篇: 博求学客 以心会友 下一篇: uC/OS-II在MC9S12XS128上的移植

分享 举报

发表评论 评论 (2 个评论)

涂鸦板