uCOS II的ucos 信号量量及任务优先级问题求助

博客访问: 394752
博文数量: 133
博客积分: 6010
博客等级: 准将
技术积分: 1583
注册时间:
APP发帖 享双倍积分
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
&uCOS II 是一个源代码公开的嵌入式实时操作系统,以其稳定可靠、高效、可移植性好,并且为占先式调度等特点,被广大工程技术人员使用。uCOS II 作为一种占先式的实时操作系统,在不少方面有着可以与商业内核相比的功能。但是uCOS II 不支持同优先级任务的调度,而实际的应用中,往往有些任务需要同优先级进行调度。如多点的温度或气压数据采集,若理解为不同的优先级任务去调度,不是一个好的逻辑设计,并且可能需要更多地考虑如何去实现不同任务的调度。另外, 如果允许同优先级任务调度,还可以解决优先级反转问题, 可以提升优先级低但占有资源的任务至申请该资源的高优先级任务的优先级, 直到低优先级的任务释放该资源, 恢复低优先级任务的优先级,高优先级的任务才占有该资源, 从而解决优先级反转问题。
1 uCOS II 的任务调度与OSTCBList结构&&& 当两个或两个以上的任务有同样优先级,内核允许一个任务运行事先确定的一段时间,叫做时间额度,然后切换给另一个任务,也叫做时间片调度。内核在满足以下条件时, 把CPU 控制权交给下一个任务就绪态的任务:
&当前任务已无事可做;
&当前任务在时间片还没有结束时已经完成。
&&& uCOS II 支持不同优先级间的任务调度,通过任务各自对应的优先级状态,快速地由OSTCBPrioTbl[OSPrioHighRdy]指针的指向切换到期望的TCB 来完成调度。这种调度与uCOS II 的OSTCBList 数据结构紧密相关, 其数据结构如图1 所示。&&&& 这是一个双向线性链表结构, 任务调度是从线性链表上的一个结点( 一个TCB 控制块) 切换到从链表上的另一个结点, 而不同的结点对应着不同的优先级, 这样来完成不同优先级的任务调度。所以要想实现uCOS II 的同优先级调度必须首先修改它的OSTCBList 数据结构,作如图2 所示的修改。&&&& 任务1 、任务2 和任务3 为不同优先级的任务,任务1、任务4 和任务5 为同优先级的任务。 当没有比任务1更高的优先级任务时, 每发生一次节拍中断, 任务1 、任务4 和任务5 就依次在运行态和就绪态切换。这里可以通过改变OSTCBPrioTbl[prio1]指向的任务TCB,也就是要修改OSSched()来实现。另外,很容易看出同优先级任务间的数据结构是一个循环链表结构, 所以TCB 也应相应的扩展两个指针域。新的数据结构将使就绪任务的TCB 在一个循环链表和线性链表上不断的切换。而对于等待任务的TCB , 也将在一个循环链表和线性链表上不断的切换。这里需要增加一个全局变量的数组OSTCBWaitTbl[OS_LOWEST_PRIO]指向相对应优先级的等待任务的TCB ,以便控制就绪任务进入等待任务或等待任务进入就绪任务时链表上任务TCB 的插入和删除。
2 对uCOS II的扩展及相关函数的修改&&& 首先扩展TCB 的指针域, 定义新的TCB 为:typedef struct os_tcb{……#if OS_TASK_PrioEqu_ENstruct OS_TCB *prioequNstruct OS_TCB*prioequP#endif……}OS_TCB增加的全局变量的数组定义为:#if OS_TASK_PrioEqu_ENOS_EXT OS_TCB OSTCBWaitTbl[OS_LOWEST_PRIO]#endif对OSTCBList 操作的每个相关函数都要修改, 主要修改以下方面的内容。① 对于链表数据结构来说, 有以下几点。
&链表的初始化及头结点。与OSTaskCreatExt()函数中的OSTCBInit()相关,需要完成同任务TCB 构成的循环链表及不同任务TCB 构成的线性链表。
&链表结点的插入和删除,与任务的状态变化有关。当任务由就绪态进入等待状态或者由等待状态进入就绪态时, 任务TCB 需要添加到相应的链表中或从对应的链表中删除。这些函数与任务由就绪态进入等待状态或者由等待状态进入就绪态相关,有OSEventTaskWait(),OSEventTo(),OSTaskRdy(),OSTaskDel()以及OSTaskChangePrio()。
② 对同优先级任务调度时,由OSTCBPrioTbl[prio]指针的移动实现TCB 的切换,这里是对循环链表的操作,可以修改OSSched()实现。③ 由时间延时满引起的超时,使等待的任务进入就绪态, 这一方面是对链表结点的插入和删除, 另外还是对任务延时的控制,需要修改OSTimeDly()和OSTimeTick()。&&& 对这几个函数修改需要注意的是, 当要对某个优先级的循环链表插入结点前或删除结点后对应的TCB 为空时, 需要对就绪列表或等待列表相应的位置位或清零。
3 解决优先级反转的方法&&& 任务对共享资源的占有和释放是通过对信号量的操作来实现的。voidTask(){……OSSemPend();//等待信号量共享资源;OSSemPost();//释放信号量……}&&& 那么,如何提升优先级低但占有资源的任务至申请该资源的高优先级任务的优先级,直到低优先级的任务释放该资源,恢复低优先级任务的优先级,高优先级的任务才占有该资源。首先这里需要改变任务优先级, 改变的优先级分别是提升优先级到高优先级和恢复任务到低优先级。显然,需要存储将要改变结果的优先级数,可以通过扩展OS_Event 来实现。typestruct{void *OSEventP……int8}OS_Event然后分别修改OSSemPend()和OSSemPost()。void OSSemPend(){……if(pevent->OSEventCnt>0){pevebt->OSEventCnt--;#if OS_TASK_PrioEqu_ENif(pevent->OSEventCnt<1){pevent->prio=OSTCBCur->}//当资源恰好分配完时,记下当前任务优先级#endifOS_EXIT_CRITICAL();*err=OS_NO_ERR;}#if OS_TASK_PrioEqu_ENelseif{OSTaskChangePrio(pevent->prio,OSTCBCUR->prio);}//当任务等待分配资源时提升占有资源任务的优先级#endif……}int8u OSSemPost(){……if(pevent->OSEventCnt<65535){pevent->OSEventCnt++;#if OS_TASK_PrioEqu_ENif(pevent->OSEventCnt<2)OSTaskChangePrio(OSTCBCur->prio,pevent->prio);}//当任务释放临界资源时恢复一个任务的优先级#endifOS_EXIT_CRITICAL();return(OS_M_ERR);……}&&& 通过对这几个函数的修改, 实现了优先级的继承,解决了优先级的反转。问题的关键是对同优先级调度的支持和对资源分配的申请与释放过程的约定。
结语&&& uCOS II 是一个良好的实时操作系统。随着更多的人使用和不断的改进, 将会有更好的应用前景。
阅读(2270) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。LwIP在,uCOS II下的实现-百合电子工作室
网站导航:
LwIP在,uCOS II下的实现
文章编号:
文章分类:
关 键 词:
文章来源:
LwIP协议栈在设计时就考虑到了将来的移植问题,因此把所有与硬件、OS、编译器相关的部份独立出来,放在ucosii&LwIPsource etlwiparch目录下。因此LwIP在uCOS II上的实现就是修改这个目录下的文件,其它的文件一般不应该修改。
&&& LwIP协议栈在设计时就考虑到了将来的移植问题,因此把所有与硬件、OS、编译器相关的部份独立出来,放在ucosii&LwIPsource etlwiparch目录下。因此LwIP在uCOS II上的实现就是修改这个目录下的文件,其它的文件一般不应该修改。下面分几部份分别说明相应文件的实现原理和过程。
2、与CPU或编译器相关的include文件:
ucosii&LwIPsource etlwiparchucosIIincludearch目录下cc.h、cpu.h、perf.h中有一些与CPU或编译器相关的定义,如数据长度,字的高低位顺序等。这应该与用户实现&C/OS II时定义的数据长度等参数是一致的。
#define&BYTE_ORDER&LITTLE_ENDIAN&&//C33209默认为小端存储系统 &&
typedef&unsigned&char&&&u8_t; &&
typedef&signed&char&&&&&s8_t; &&
typedef&unsigned&short&&u16_t; &&
typedef&signed&short&&&&s16_t; &&
typedef&unsigned&int&&&&u32_t; &&
typedef&signed&int&&&&&&s32_t; &&
此外还有一点:一般情况下C语言的结构体struct是4字节对齐的,但是在处理数据包的时候,LwIP使用的是通过结构体中不同数据的长度来读取相应的数据的,所以,一定要在定义struct的时候使用_packed关键字,让编译器放弃struct的字节对齐。LwIP也考虑到了这个问题,所以,在它的结构体定义中有几个PACKED_FIELD_xxx宏,默认的时候这几个宏都是空的,可以在移植的时候添加不同的编译器所对应的_packed关键字。比如在Skyeye(C33209)上对应gcc编译器的定义:
#define&PACK_STRUCT_FIELD(x)&x&__attribute__((packed)) &&
#define&PACK_STRUCT_STRUCT&__attribute__((packed)) &&
#define&PACK_STRUCT_BEGIN &&
#define&PACK_STRUCT_END&&
3、sys_arch操作系统相关部份:
&&& sys_arch.[ch]中的内容是与OS相关的一些结构和函数,主要可以分为四个部份:
(1)&&& sys_sem_t 信号量
LwIP中需要使用信号量通信,所以在sys_arch中应实现信号量结构体和处理函数:
struct&&sys_sem_t &&
&&&&sys_sem_new()&&&&&&&&&&&&&&
&&&&sys_&sem&_free()&&&&&&&&&&&&&&
&&&&sys_&sem&_signal()&&&&&&&&&&
&&&&sys_&arch_sem&_wait()&&&&&&
由于&C/OSII已经实现了信号量OS_EVENT的各种操作,并且功能和LwIP上面几个函数的目的功能是完全一样的,所以只要把&C/OSII的函数重新包装成上面的函数,就可以直接使用了。
(2)&&& sys_mbox_t 消息
LwIP使用消息队列来缓冲、传递数据报文,因此要在sys_arch中实现消息队列结构sys_mbox_t,以及相应的操作函数:
sys_mbox_new()&&&&&&&&&&&&
sys_mbox_free()&&&&&&&&&&&&
sys_mbox_post()&&&&&&&&&&&&
sys_arch_mbox_fetch()&&&&&&&&
&C/OSII同样实现了消息队列结构OSQ及其操作,但是&C/OS-II没有对消息队列中的消息进行管理,因此不能直接使用,必须在&C/OS-II的基础上重新实现。为了实现对消息的管理,我们定义了以下结构:
typedef&struct&{ &&
&&&&&&&&OS_EVENT*&&&pQ; &&
&&&&&&&&void*&pvQEntries[MAX_QUEUE_ENTRIES]; &&
}&sys_mbox_t; &&
在以上结构中,包括OS_EVENT类型的队列指针(pQ)和队列内的消息(pvQEntries)两部分,对队列本身的管理利用&C/OS-II自己的OSQ操作完成,然后使用&C/OS-II中的内存管理模块实现对消息的创建、使用、删除回收,两部分综合起来形成了LwIP的消息队列功能。
(3)&&& sys_arch_timeout 函数
LwIP中每个与外界网络连接的线程都有自己的timeout属性,即等待超时时间。这个属性表现为每个线程都对应一个sys_timeout结构体队列,包括这个线程的timeout时间长度,以及超时后应调用的timeout函数,该函数会做一些释放连接,回收资源的工作。如果一个线程对应的sys_timeout为空(NULL),说明该线程对连接做永久的等待。
timeout结构体已经由LwIP自己在sys.h中定义好了,而且对结构体队列的数据操作也由LwIP负责,我们所要实现的是如下函数:
struct&sys_timeouts&*&sys_arch_timeouts(void)&&
这个函数的功能是返回目前正处于运行态的线程所对应的timeout队列指针。timeout队列属于线程的属性,因此是OS相关的函数,只能由用户实现。
(4)&&& sys_thread_new 创建新线程
LwIP可以是单线程运行,即只有一个tcpip线程(tcpip_thread),负责处理所有的tcp/ucp连接,各种网络程序都通过tcpip线程与网络交互。但LwIP也可以多线程运行,以提高效率,降低编程复杂度。这时就需要用户实现创建新线程的函数:
void&sys_thread_new(void&(*&thread)(void&*arg),&void&*arg);&&
在&C/OS II中,没有线程(thread)的概念,只有任务(Task)。它已经提供了创建新任务的系统API调用OSTaskCreate,因此只要把OSTaskCreate封装一下,就可以实现sys_thread_new。需要注意的是LwIP中的thread并没有&C/OS II中优先级的概念,实现时要由用户事先为LwIP中创建的线程分配好优先级。
4、lib_arch中库函数的实现:
&&& LwIP协议栈中用到了8个外部函数,这些函数通常与用户使用的系统或编译器有关,因此留给用户自己实现。如下:
u16_t&htons(u16_t&n);&&&&&&&
u16_t&ntohs(u16_t&n); &&
u32_t&htonl(u32_t&n);&&&&&&&&
u32_t&ntohl(u32_t&n); &&
int&strlen(const&char&*str);&&&&&&
int&strncmp(const&char&*str1,&const&char&*str2,&int&len);&&&&
void&bcopy(const&void&*src,&void&*dest,&int&len);&&&&&&
void&bzero(void&*data,&int&n);&&&&&&&&&&
&&& 前四个函数通常由用户自己实现。在我的系统中,由于使用了gcc编译器,gcc的lib库里已经有了两个字符串操作函数。若用户的编译器的库中没有这些函数,需要自己编写。
5、网络设备驱动程序:
&&& 在我的系统中使用的网络芯片为RealTek的8019as芯片,这是ISA 10BASE-T的以太网芯片,与Ne2k兼容。所以目前实现的网络设备驱动是针对Ne2k的,其它类型的网络芯片驱动可以在LwIP的网站上找到。LwIP的网络驱动有一定的模型,ucosii&LwIPsource etlwiparchucosII etif 中的ne2kif.c文件即为驱动的模板,用户为自己的网络设备实现驱动时应参照此模板。
&&& 在LwIP中可以有多个网络接口,每个网络接口都对应了一个struct netif,这个ne2kif包含了相应网络接口的属性、收发函数。LwIP调用ne2kif的方法netif-&input()及netif-&output()进行以太网packet的收、发等操作。在驱动中主要做的,就是实现网络接口的收、发、初始化以及中断处理函数。驱动程序工作在IP协议模型的网络接口层,它提供给上层(IP层)的接口函数如下:
void&low_level_init&(struct&netif&*netif) &&
void&ne2k_recv_packet&(struct&netif&*netif) &&
err_t&ne2k_send_packet&(struct&netif&*netif,&struct&pbuf&*p,&struct&ip_addr&*ipaddr) &&
void&ne2k_isr&(void); &&
&&& 以上的函数都可以分为协议栈本身的处理和对网络接口硬件的操作两部份,但硬件操作是对上层屏蔽的,具体参见RTL8019as、DM9008等Ne2k网络芯片的数据手册。驱动程序可以到LwIP的网站下载。
6、应用实例的建立和测试
&&& 做完上面的移植修改工作以后,就可以在uCOS II中初始化LwIP,并创建TCP或UDP任务进行测试了。这部份完全是C语言的实现,因此这部份在ez80和ARM7上基本都是一样的。值得注意的是LwIP的初始化必须在uCOS II完全启动之后也就是在任务中进行,因为它的初始化用到了信号量等OS相关的操作。关键部份的代码和说明如下:
void&start_kernel(void) &&
&&&&int&&&&&LineNo10&=&0; &&
&&&&int&&&&&LineNo11&=&1; &&
&&&&int&&&&&LineNo12&=&2; &&
&&&&int&&&&&LineNo13&=&3; &&
&&&&int&&&&&LineNo14&=&4; &&
&&&&OSInit(); &&
&&&&OSTaskCreate(lwip_init_task,&&LineNo10,&&lwip_init_stk[TASK_STK_SIZE-1],&0); &&
&&&&OSTaskCreate(usr_task,&LineNo14,&usr_stk[TASK_STK_SIZE-1],20); &&
&&&&vRTCStart(); &&
&&&&OSStart(); &&
&&&&while(1); &&
主程序中创建了lwip_init_task初始化LwIP任务(优先级0)和usr_task用户任务(优先级20)。lwip_init_task任务中除了初始化硬件时钟和LwIP之外,还创建了tcpip_thread(优先级5)和tcpecho_thread(优先级6)。实际上tcpip_thread才是LwIP的主线程,多线程的Berkley API也是基于这个线程实现的,即上面的tcpecho_thread线程也要依靠tcpip_thread线程来与外界通信,这样做的好处是编程简单,结构清晰。
实用Berkley API实现的tcpecho_thread是一个TCP echo服务器,监听7号端口,程序框架如下:
void&tcpecho_thread(void&*arg){ &&
&&&&conn&=&netconn_new(NETCONN_TCP);&&&&
&&&&netconn_bind(conn,&NULL,&7);&&&&&&&&&&
&&&&netconn_listen(conn);&&&&&&&&&&&&&&&&&&
&&&&while(1){ &&
&&&&&&&&newconn&=&netconn_accept(conn);&&&&&&
&&&&&&&&buf&=&netconn_recv(newconn)&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&netconn_write(newconn,&data,&len,&NETCONN_COPY);&&&
&&&&&&&&netconn_delete(newconn);&&&&&&&&&&&&&&
&&& 编译运行后,用ping ip地址命令可以得到ICMP reply响应。用telnet ip地址 7(登录7号端口)命令可以看到echo server的回显效果。说明ARP、ICMP、IP、TCP协议都已正确运行。
最新开源项目
本站相关产品&&&
本站程序由百合电子工作室开发和维护
Copyright @ baihe electric studio下次自动登录
现在的位置:
& 综合 & 正文
UCOS II多任务切换例程(二)
原链接:http://
五. 任务状态:uCOS II主要有五种任务状态,睡眠态就是挂起态,阻塞态和延时态这里统一为等待状态。增加了一个被中断状态。UC/OS-Ⅱ总是建立一个空闲任务,这个任务在没有其它任务进入就绪态时投入运行。这个空闲任务[OSTaskIdle()]永远设为最低优先级空闲任务OSTaskIdle()什么也不做,只是在不停地给一个32位的名叫OSIdleCtr的计数器加1,统计任务使用这个计数器以确定现行应用软件实际消耗的CPU时间。空闲任务不可能被应用软件删除。 睡眠态(DORMANT)指任务驻留在空间之中,还没有交给μC/OS-Ⅱ管理,把任务交给μC/OS-Ⅱ是通过调用下述两个函数之一:OSTaskCreate()或OSTaskCreateExt()。当任务一旦建立,这个任务就进入就绪态准备运行。任务的建立可以是在多任务运行开始之前,也可以是动态地被一个运行着的任务建立。如果一个任务是被另一个任务建立的,而这个任务的优先级高于建立它的那个任务,则这个刚刚建立的任务将立即得到CPU的控制权。一个任务可以通过调用OSTaskDel()返回到睡眠态,或通过调用该函数让另一个任务进入睡眠态。调用OSStart()可以启动多任务。OSStart()函数运行进入就绪态的优先级最高的任务。就绪的任务只有当所有优先级高于这个任务的任务转为等待状态,或者是被删除了,才能进入运行态。正在运行的任务可以通过调用两个函数之一将自身延迟一段时间,这两个函数是OSTimeDly()或OSTimeDlyHMSM()。这个任务于是进入等待状态,等待这段时间过去,下一个优先级最高的、并进入了就绪态的任务立刻被赋予了CPU的控制权。等待的时间过去以后,系统服务函数 OSTimeTick()使延迟了的任务进入就绪态(见3.10节,时钟节拍)。正在运行的任务期待某一事件的发生时也要等待,手段是调用以下 3个函数之一:OSSemPend(),OSMboxPend(),或OSQPend()。调用后任务进入了等待状态(WAITING)。当任务因等待事件被挂起(Pend),下一个优先级最高的任务立即得到了CPU的控制权。当事件发生了,被挂起的任务进入就绪态。事件发生的报告可能来自另一个任务,也可能来自中断服务子程序。正在运行的任务是可以被中断的,除非该任务将中断关了,或者μC/OS-Ⅱ将中断关了。被中断了的任务就进入了中断服务态(ISR)。响应中断时,正在执行的任务被挂起,中断服务子程序控制了CPU的使用权。中断服务子程序可能会报告一个或多个事件的发生,而使一个或多个任务进入就绪态。在这种情况下,从中断服务子程序返回之前,μC/OS-Ⅱ要判定,被中断的任务是否还是就绪态任务中优先级最高的。如果中断服务子程序使一个优先级更高的任务进入了就绪态,则新进入就绪态的这个优先级更高的任务将得以运行,否则原来被中断了的任务才能继续运行。当所有的任务都在等待事件发生或等待延迟时间结束,μC/OS-Ⅱ执行空闲任务(idle task),执行OSTaskIdle()函数。六. 任务切换:Context Switch
在有的书中翻译成上下文切换,实际含义是任务切换,或CPU寄存器内容切换。当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态(Context),即CPU寄存器中的全部内容。这些内容保存在任务的当前状况保存区(Task’s Context Storage area),也就是任务自己的栈区之中。(见图2.2)。入栈工作完成以后,就是把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行。这个过程叫做任务切换。任务切换过程增加了应用程序的额外负荷。CPU的内部寄存器越多,额外负荷就越重。做任务切换所需要的时间取决于 CPU有多少寄存器要入栈。实时内核的性能不应该以每秒钟能做多少次任务切换来评价。
七. 任务调度分析:uCOS II提供最简单的实时内核任务调度,简单,因此也只支持优先级抢占任务调度,不支持时间片轮训调度算法,不支持优先级逆转。uCOS II总是运行进入就绪态任务中优先级最高的那一个。确定哪个任务优先级最高,下面该哪个任务运行了的工作是由调度器(Scheduler)完成的。任务级的调度是由函数OSSched()完成的。中断级的调度是由另一个函数OSIntExt()完成的,这个函数将在以后描述。uCOS II任务调度所花的时间是常数,与应用程序中建立的任务数无关。在uCOS中曾经是先得到OSTCBHighRdy然后和OSTCBCur做比较。因为这个比较是两个指针型变量的比较,在8位和一些16位微处理器中这种比较相对较慢。而在μC/OS-Ⅱ中是两个整数的比较。并且,除非用户实际需要做任务切换,在查任务控制块优先级表OSTCBPrioTbl[]时,不需要用指针变量来查OSTCBHighRdy。综合这两项改进,即用整数比较代替指针的比较和当需要任务切换时再查表,使得uCOS II比uCOS在8位和一些 16位微处理器上要更快一些。为实现任务切换,OSTCBHighRdy必须指向优先级最高的那个任务控制块OS_TCB,这是通过将以 OSPrioHighRdy为下标的OSTCBPrioTbl[]数组中的那个元素赋给OSTCBHighRdy来实现的[L3.8(4)]。最后宏调用 OS_TASK_SW()来完成实际上的任务切换[L3.8(6)]。任务切换很简单,由以下两步完成,将被挂起任务的微处理器寄存器推入堆栈,然后将较高优先级的任务的寄存器值从栈中恢复到寄存器中。在uCOS II中,就绪任务的栈结构总是看起来跟刚刚发生过中断一样,所有微处理器的寄存器都保存在栈中。换句话说,μC/OS-Ⅱ运行就绪态的任务所要做的一切,只是恢复所有的CPU寄存器并运行中断返回指令。为了做任务切换,运行 OS_TASK_SW(),人为模仿了一次中断。多数微处理器有软中断指令或者陷阱指令TRAP来实现上述操作。中断服务子程序或陷阱处理(Trap hardler),也称作事故处理(exception handler),必须提供中断向量给汇编语言函数OSCtxSw()。 OSCtxSw()除了需要OS_TCBHighRdy指向即将被挂起的任务,还需要让当前任务控制块OSTCBCur指向即将被挂起的任务。OSSched ()的所有都属临界段代码。在寻找进入就绪态的优先级最高的任务过程中,为防止中断服务子程序把一个或几个任务的就绪位置位,中断是被关掉的。为缩短切换时间,OSSched()全部代码都可以用汇编语言写。为增加可读性,可移植性和将汇编语言代码最少化,OSSched()是用C写的。
任务切换的相关函数:与CPU体系相关,汇编完成。1. OSStartHighRdy() 执行优先级最高的任务2. OSCtxSw()
完成任务的上下文切换3. OSIntCtxSw()
中断后的上下文切换4. OSTickISR()
中断服务程序启动
八. uCOS II的初始化:OSInit()建立空闲任务idle task,这个任务总是处于就绪态的。空闲任务OSTaskIdle()的优先级总是设成最低。这两个任务的任务控制块(OS_TCBs)是用双向链表链接在一起的。OSTCBList指向这个链表的起始处。当建立一个任务时,这个任务总是被放在这个链表的起始处。换句话说,OSTCBList总是指向最后建立的那个任务。链的终点指向空字符NULL(也就是零)。因为这两个任务都处在就绪态,在就绪任务表OSRdyTbl[]中的相应位是设为1的。还有,因为这两个任务的相应位是在OSRdyTbl[]的同一行上,即属同一组,故OSRdyGrp中只有1位是设为1的。 uCOS II还初始化了4个空数据结构缓冲区,如图F3.8所示。每个缓冲区都是单向链表,允许uCOS II从缓冲区中迅速得到或释放一个缓冲区中的元素。控制块OS_TCB的数目也就自动确定了。当然,包括足够的任务控制块分配给统计任务和空闲任务。
uCOS II内核调度分析
vxWorks内核调度分析
1.只支持基于优先级的抢占式调度算法,不支持时间片轮训;
采用工作队列 workQword的方式调度;
2.64个优先级,只能创建64个任务,用户只能创建56个任务;
根据用户指定,动态分配堆栈,可以创建任意多个任务;
3.每个任务优先级都不相同。
4.不支持优先级逆转;
支持优先级逆转,TCB保存两个优先级;
5.READY队列通过内存映射表实现快速查询。效率非常高;
支持抢占与时间片轮训的任务调度方式;
6.支持时钟节拍;
通过编译开关实现对多cpu体系结构的支持。
7.支持信号量,消息队列,事件控制块,事件标志组,消息邮箱任务通讯机制;
队列采用FIFO或者优先级的双向链表实现;
8.支持中断嵌套,中断嵌套层数可达255层,中断使用当前任务的堆栈保存上下文;
支持中断嵌套,中断使用专用的堆栈保存上下文;
9.每个任务有自己的堆栈,堆栈大小用户自己设定;
任务是基于类,对象的管理方式;
10.支持动态修改任务优先级;
支持动态修改任务优先级;
11.任务TCB为静态数组,建立任务只是从中获得一个TCB,不用动态分配,释放内存;
任务的TCB保存在任务的堆栈里;
12.任务堆栈为用户静态或者动态创建,在任务创建外完成,任务创建本身不进行动态内存分配;
每个任务有自己的堆栈,堆栈大小用户自己设定;
13.任务的总个数(OS_MAX_TASKS)由用户决定;
14.0优先级最高, 63优先级最低;
任务的优先级从0—255,0优先级最高,允许多个任务相同优先级;
15.有一个优先级最低的空闲任务,在没有用户任务运行的时候运行.
系统没有空闲任务执行;
&&&&推荐文章:
【上篇】【下篇】【deven】对uCOS-II中定时器的改进-中国学网-中国IT综合门户网站-提供健康,养生,留学,移民,创业,汽车等信息
【deven】对uCOS-II中定时器的改进
来源:互联网 更新时间: 12:45:04 责任编辑:李志喜字体:
【deven】对uCOS-II中定时器的改进作者:deven
硬件环境:LPC2220;键盘采用ZLG7290,中断信号接EINT3,用IIC读取按键数据;编码器采用1000个信号的,Z相接EINT0,A相接CAP1.2,B相接CAP1.3。
一。发生了什么问题&&& 不知道为何,在按键中断中POST一个信号量,虽然在任务中得到了响应,但是跟踪发现,切换到任务之后并没有执行,而是不停地进入timer0中断。键盘响应有延迟是可以忍受的,但是在其他中断处理中就可能影响到系统性能。例如我用光电编码器的时候信号触发比较频繁,从中断到任务中得到实际响应会变慢。&&& 为了能及时响应任务,并且减少中断触发频率,必须对系统的定时器进行改进。
二。时钟中断的作用&&& 起初看到这个时钟中断就想当然的任务是操作系统调度所必须的,然后进一步思考后发现错了。在一般的分时系统中,为了实现时间片的轮转,在一定时间后必须将处理器切换到下一个任务,因此必须有定时器定时地切换任务。而在uCOS-II这样的优先级调度的系统中,是在高优先级任务放弃cpu之后才进行调度进入低优先级任务的。&&& 在uCOS-II系统中时钟中断又起到了一个什么样的作用呢?先来看看任务在什么样的情况下放弃cpu,在什么样的情况下得到cpu。&&& 放弃cpu可以由OSSemPend、OSMutexPend、OSFlagPend、OSMboxPend、OSQPend、OSTimeDly、OSTimeDlyHMSM几个函数触发。这里最后两个函数是跟时间相关,其他函数都是跟事件相关。所以一个cpu要再次得到执行,可以是因为延迟时间到了,或者是调用了OSTimeResume,也可以是因为事件的触发。延时很明显是需要用到定时器的,而信号是不需要定时器的。&&& 所以定时器在uCOS-II系统中仅仅起到延时的作用。
三。怎么改进&&& 根据定时器在系统中的作用,只要实现定时器的延时功能即可。实现延时是必须要定时器的,只能减少定时器中断触发的频率,从而改进系统性能。一个马上能想到的想法&&调用延时函数的时候开启,定时器到时间产生中断进行任务切换,同时定时器关闭。但是这个想法仍须改进。当定时器正处于延时状态时,又有一个新的延时请求触发了,这个时候该怎么办?我想一旦认识到了这个问题之后,大家都会有跟我相同的想法&&执行最短的一段延时。当前定时器剩余的tick数跟请求的延时tick数进行比较,如果剩余tick数小于请求tick数,则在当前定时完成之后,继续定时,从而完成请求的延时操作。在定时中断中给每个任务减去已经走过的tick数,然后进行下一个定时。&&& 实际应用中这种延时重叠的情况是随即发生的,请求重叠的个数也是不定的。很多人首先会想到用链表来保存各个定时器的信息,但是TCB结构体中已有剩余tick数的成员在了,所以不必舍近求远了。&&& tick的单位是多少?还是1/OS_TICKS_PER_SEC秒吗?如果使用定时器中的计数值的单位,那么当tick值比较小的时候执行执行一条指令就会使定时器中的计数值发生变化,短短几个tick还需要释放cpu调度到其他进程吗?还没完成调度定时中断已经触发了。所以tick的单位仍旧使用1/OS_TICKS_PER_SEC,这样程序也可以与以前的保持兼容。
相关文章:
<a href="/cse/search?q=<inputclass="s-btn"type="submit"text="<inputclass="s-btn"type="submit"text="<buttonhidefocusclass="s-btnjs-ask-btn"text="我要提问
<a href="/cse/search?q=

我要回帖

更多关于 ucosii优先级 的文章

 

随机推荐