在阅读完uC/OS-III(V3.03.01)和FreeRTOS(V10.0.1)的源码后,我对RTOS有了较深的认识。现将它们之间的一些区别总结出来,有利于大家理解这两个RTOS。
1、uCOS-III中所有的内核对象(如任务控制块、消息队列、信号量等)都是静态创建的,需要用户提供。FreeRTOS中的内核对象支持动态和静态两种创建方法。 (PS: 其实系统提不提供动态创建功能并不那么重要,因为在静态创建的方法的基础上加入内存管理机制,就能自已封装实现动态创建函数)
2、uCOS-III中的任务状态较多,因为它存在“基本状态+挂起状态”这类状态,FreeRTOS中挂起态是个单独的状态。在FreeRTOS中,如果suspend一个正在阻塞的任务,API内部会把任务从相应阻塞表中删除,并将其挂在xSuspendedTaskList上,当该任务被resume后,它就是就绪态,而不会重新返回阻塞态。而uCOS-III中的任务即便在阻塞时被suspend了,它依然处于阻塞态(即等待某个事件发生),如果在suspend的过程中事件发生了,它将解除阻塞态,变为纯粹的挂起态;如果在resume后,该事件仍未发生,它将解除挂起态,变为阻塞态。 (PS: 我感觉uCOS-III中的“挂起”更能称之为“挂起”)
3、为了实现中断和任务的同步,需要在中断中进行post操作,uC/OS-III为了减少中断执行的时间,提高系统中断响应的实时性,设计了OS_TickTask和OS_IntQTask,这样原本在中断里需要进行的一些较为耗时的操作就被放到了任务级代码中执行了。而FreeRTOS并没有这样的设计。 (PS: 我觉得,从这一点上,可以看出uC/OS-III的实时性要比FreeRTOS好。 另外,可能有的同学不理解为什么中断执行时间少了,系统的实时性就好了。这是因为系统实时性的一个关键指标就是中断延迟响应的时间。某个中断可能会被延迟响应的时间,受系统关中断时间的影响,也会受其它同等优先级或者高优先级中断执行时间的影响,所以减少某个中断的执行时间,将有助于减少其它中断的延迟响应时间)
4、uC/OS-III和FreeRTOS的任务切换都是利用的PendSV中断。 在FreeRTOS的PendSV中断中,它会计算就绪的最高优先级的任务,再去进行上下文切换。而uC/OS-III在触发PendSV中断前,会计算好已就绪的最高优先级的任务,放在OSTCBHighRdyPtr中,这样在PendSV中断中就不用计算就绪的最高优先级的任务是谁了。所以uC/OS-III中PendSV中断的执行时间更短,这有利于提高系统的实时性。
5、uCOS-III的任务操作句柄就是任务控制块TCB的指针。FreeRTOS中单独设置了任务操作句柄这种数据类型,它实质上也是TCB的指针。表面上看,多此一举,但其实这种设计对用户是友好的,用户不需要了解TCB这种内核数据结构的存在,就可以操作任务了。
6、对于时间片轮转调度的功能。 FreeRTOS是每个时间片(即每个systick中断里)发生同优先级的任务切换。 而uCOS-III中每个任务能保持的时间片可以单独设置,需要在任务初始化时作为形参传入。这样做的坏处是对用户不太友好(API的形参如果太多,应用开发人员接受起来有些麻烦);这样做的好处是不会在每个时间片都发生任务的切换(任务切换是需要开销的),提高了总的CPU利用率。 另外,uC/OS-III中,由于可以对每个任务的时间片分别进行设置和修改,所以可以很方便地调节同优先级下每个任务的CPU占用率,尽管两个任务的优先级是一样的,但是有个任务比较重要,我们希望让它的CPU占用率高一点,这时只要把它的时间片设置得大一点即可。而在FreeRTOS中同优先级下的每个任务对CPU的占用率都只能是一样的。
7、uCOS-III内核中的链表大多是不循环的双向链表(有头有尾),在插入和删除操作时,要考虑特殊情况(比如插入表头、插入表尾等特殊情况)。 而FreeRTOS内核中的链表为双向循环链表,并引入了xListEnd保证了链表永远非空,所以每个元素的插入和删除都是作为表中的一般元素(非表头和表尾)进行的,操作效率要比uC/OS-III高一些。
8、对于修改任务优先级的操作,FreeRTOS和uCOS-III都是可选项。 对于uCOS-III,在修改任务优先级时,如果任务处于阻塞态,内核会将pend_data从阻塞对象的pend_list上删除,并重新按照新的任务优先级插入,即调用OS_PendListInsertPrio()。 而FreeRTOS对于修改阻塞态的任务的优先级,似乎只是修改了TCB里的优先级字段就完事儿了,这点虽然不会造成较大的影响,但是违背了“优先级高的任务优先获得阻塞对象”的设计原则。不知这是否是bug。
9、uCOS-III的信号量是没有上限的,只要post,它的信号量值就会增加(不能溢出)。而FreeRTOS中的信号量基于Queue_t实现,其队列容量(uxLength)将作为信号量的上限。
10、uCOS-III支持PendAbort,而FreeRTOS不支持。
11、uCOS-III中的软件定时器是靠对systick分频实现的,与OS_TickTask运行原理类似,都采用的哈希散列表组织定时器。 FreeRTOS中的定时器是按照超时时间组织成了有序链表。prvTimerTask与LwIP中的tcpip_thread一样,每次都试图按照当前超时链表上的最近超时时间阻塞在queue上。 FreeRTOS的定时器机制,单独设计了一个queue,所有Timer相关的API(函数或宏)本质上都是对该queue的一次消息投递,由prvTimerTask来完成对消息的解析并处理。这样做的好处就是:只在一个线程中进行与Timer相关数据的操作,省去了互斥带来的开销。
13、uCOS-III的许多API,都设计了OS_ERR* 的参量,它可以用来向应用程序传递许多出错信息,虽然这会增加API的复杂度,给应用程序开发人员带来一些麻烦。比如OSQueuePend,通过&err,我们可以知道这次pend操作是成功还是失败的,如果失败,是怎么失败的(比如:超时、队列被删除、队列被PendAbort等)。有的人可能会说,只要观察函数返回值,就可以知道此次pend操作时成功还是失败的,没必要设置OS_ERR* 这个参量。但是这样做无法得知失败的具体原因。 FreeRTOS在设计API时就没有设计这个错误参量,应用程序开发人员只能通过API函数的返回值来判断操作是否成功,但是如果失败,则无法得到更多的关于失败的信息。 (PS: 从这一点上可以看出,uC/OS-III提供了更健壮的内核)
14、对于消息队列,uCOS-III只支持出队阻塞,不支持入队阻塞,即OSQueuePost这个函数在消息队列已满的情况下,不会自已阻塞去等待队列里腾出空间,而是直接返回邮箱已满的错误信息,用户需要及时检查返回的错误码,进行处理。 FreeRTOS中既支持出队阻塞,也支持入队阻塞。 (PS: 在uC/OS-III中尽管不支持post阻塞,但如果必须实现post阻塞(比如LwIP移植中的sys_arch_mbox_post接口实现)也是很容易的,只要利用信号量和消息队列再加上一个阻塞队列专门用来记录等待post的任务即可,这是对uC/OS-III内核进行二次开发)。
15、uC/OS-III使用消息指针代表消息,FreeRTOS使用消息内容的完整备份代表消息,在uC/OS-III中,投递了消息以后,要保证该指针在消息被利用前一直有效(即保证消息内容不被删除、覆盖),在FreeRTOS中则无所谓,另外在FreeRTOS中也可以将消息指针当做消息内容传递,这样就可以模拟出跟uC/OS-III一样的效果了。 如此看来,FreeRTOS的消息设计更加灵活。但是要注意,使用指针的好处是避免消息的拷贝,这可以提高内核的处理效率,尤其是消息内容较大或者消息需要辗转多个消息队列的时候。所以我觉得像uC/OS-III这样设计就挺好的,没必要考虑其他情况。 另外,uC/OS-III的消息是以OS_MSG结构体存在的,里面除了消息指针以外,还包含了时间戳,提供了更丰富的信息。不过要注意,由于它使用了OS_MSG结构体承载消息,而OSQueuePost函数内部是通过预先定义好的OS_MSG结构体内存池去获取这种结构的,所以需要用户在编译工程代码时根据自己使用到的消息的规模去配置内存池大小,一些人以为从uC/OS-II过渡到uC/OS-III的好处就是再也不用事先配置每种内存池的大小了(当应用需求变化,总是需要调整,比较麻烦),但千万别忘了有一个(也是唯一一个)内存池需要提前配置好大小。
16、在消息投递时,如果有任务在消息队列的pend列表中等待,uC/OS-III的做法是直接将该消息post给等待的任务并把它就绪,整个消息不会经过消息队列。而FreeRTOS的做法是将该消息放置到消息队列中,然后检查是否有任务正等待接收消息,如果有,就将其就绪,由就绪的任务去主动获取该消息。 FreeRTOS的做法实现了投递消息与取出消息的解耦,但这带来了一个问题,就是当某个任务投递完一个消息,并使A任务就绪了,而在A任务执行前,又有一个高优先级的B任务从这个消息队列中取出了该消息,那么A任务在以后试图从消息队列中取出消息时,会出现失败,这是FreeRTOS中所有内核对象的pend操作都可能会出现的情况,内核的做法是会重新计算超时时间,只要没超时,就重新阻塞(按新的阻塞时间),这种设计会降低内核的执行效率。在uC/OS-III中,每一次的消息post操作,要么消息被post到了消息队列上,要么消息被post给了任务,操作结果是明确的,操作过程是高效的。 (PS: 我觉得,从这一点可以看出uC/OS-III的内核要比FreeRTOS具有更好的效率、行为可预测性)
17、uCOS-III的pend函数用opt字段区分是try pend还是block pend,如果是block pend,可设置超时时间,如果超时时间为0,则代表永久pend。 FreeRTOS的pend函数没有opt字段,只有超时字段,如果它为0,则表示不等待,为try pend,如果为其他值,则它为超时时间,如果它为最大值(portMAX_DELAY),并且配置文件中使能了suspend task的功能,则portMAX_DELAY表示永久等待,如果没使能,则表示portMAX_DELAY个ticks将作为超时时间。 可见FreeRTOS中,在不使能task suspend的情况下,是不允许有任务永久阻塞的(可能是为了应用程序的安全性考虑)。
18、FreeRTOS提供了TaskNotify机制,用它可以更轻便地实现:单对单或多对单的简单同步和通信功能。在uC/OS-III中虽然没有TaskNotify机制,但是提供了TaskSem和TaskQueue机制,可以完成同样的效果。 (PS: 为了跨系统的可移植性,最好不要使用这些特殊机制)
总结:FreeRTOS功能更丰富、更易用;uC/OS-III的实时性更好、效率更高、健壮性更好。 其实RTOS最主要的功能就是任务调度,其它功能都可以自己开发,难度不大。单独从任务调度器的角色出发去对比这两个RTOS,我觉得uC/OS-III更漂亮、更优秀。 uC/OS-III通过的安全认证比FreeRTOS要多,FreeRTOS的代码书写是不符合一些标准的。在FreeRTOS的基础上建立了另外两个RTOS:SafeRTOS、OpenRTOS,它们具有更好的安全性,通过了更多的检验和标准,但是与FreeRTOS不一样,需要收费。
在过去EETimes的RTOS市场占有率调查中,FreeRTOS常年稳居第一,这与它完全免费、开源社区比较活跃的特点有关。再加上FreeRTOS的创始团队现在与亚马逊合作,FreeRTOS的系统功能将更加丰富,将拥有更多商业合作伙伴,用户数量群将继续扩大,目测FreeRTOS的发展前景会更好。
|