当前位置:首页 > 嵌入式 > 嵌入式硬件
[导读]摘要:Small RTOS51是一款重要的小型实时内核,消息队列是其提供的重要任务间通信的机制。针对其消息队列实现代码中的缺陷以及可能导致的消息丢失这一严重问题,从操作系统

摘要:Small RTOS51是一款重要的小型实时内核,消息队列是其提供的重要任务间通信的机制。针对其消息队列实现代码中的缺陷以及可能导致的消息丢失这一严重问题,从操作系统等待与唤醒机制理论的角度出发,剖析Small RTOS51内核在消息队列甚至互斥型信号量等实现机制上的漏洞所在;进一步指出原内核实现方式的修改方法,以及《Small RTOS51中消息队列的一处隐患》作者提出的第2种修改方法的完美实现。 关键词:Small RTOS51 消息队列 唤醒模型 隐患分析 引言   贵刊2005年第7期《Small RTOS51中消息队列的一处隐患》一文,对Small RTOS51V1.12.1版本的消息队列机制进行了周密的分析,不但找出了问题所在,也提出了相应的两种解决方法\[1\]。实时嵌入式系统对于安全性有很高的要求,作为实时嵌入式系统的内核,不但要求精简高效,更要加强安全,防止因操作系统出错而在应用领域导致灾难性的后果。因此原文作者所做的工作极有价值,同时也感谢贵刊对这一领域的高度重视。   因为这一问题涉及到内核的等待与唤醒机制,并且正是由于对内核的等待与唤醒机制的理解与运用不同,才导致了问题的出现,所以本文从操作系统理论的高度以及目前主流的实时内核的实现方法两方面入手论述这一问题,并揭示如何才能完美实现原文的第2种方法。 1 内核唤醒机制的三种模型   当利用系统调用接口获取资源时,如果资源不满足,系统调用可以返回错误,也可以根据选项悬挂等待;当有任务释放资源从而资源可以满足时,就要将资源等待队列中的相关任务唤醒。唤醒模型有三种\[2\]:第1种,将该资源等待队列中的任务全部唤醒,让这些任务与系统中的其他任务平等竟争资源。这种策略会使系统在一段时间内繁忙,因为最终只有一个任务获取到资源,其他任务可能将经历一个从就绪态到运行态再到阻塞态的过程。这种现象在操作系统理论上称为“千军万马奔腾”。就目前的一些主流实时内核 VxWorks、Nucleus、uC/OS?II等来讲,都没有采用这种策略。   第2种,将该资源等待队列中的一个任务唤醒,依据所采用的策略不同,可以是等待任务中优先级最高的,也可以是第1个进入等待队列中的任务。这个任务被唤醒后将和系统中的其他任务一起竞争这个资源。如果这个任务最终没有竞争到这个资源,它将再次进入该资源的等待队列并进行任务调度。   第3种,将该资源等待队列中的一个任务唤醒,依据所采用的策略不同,可以是等待任务中优先级最高的,也可以是第1个进入等待队列中的任务,这点和第2 种方法是一样的。和第2种情况不同的是,这个任务被指定为资源的获得者。主流实时内核VxWorks、Nucleus、uC/OS?II等都采用这种策略。以VxWorks为例,其内核文档指出\[3\]:“任务或ISR调用msgQSend()向消息队列发送消息。此时如果没有任务在等待该队列中的消息,那么该消息进入消息队列的缓冲;如果有任务等待该队列的消息,那么这个消息立即提交给第1个等待的任务。”这段话有两方面的含义:① 明确指出第1个等待的任务获得资源;② 第1个等待的任务获得资源的方式是直接从消息的发送者那里获得,也就是说这个消息将不进入消息队列进行缓冲,消息在发送者和接收者之间进行手把手的传递。对于这种机制的实现,可以以著名的源代码公开的实时嵌入式操作系统Nucleus为例。下面是Nucleus内核关于接收消息的一段精彩的代码: else {   /* 消息队列为空,决定是否悬挂等待*/   if (suspend) {     /* 增加等待该消息队列的任务数量 */     queue -> qu_tasks_waiting++;     /* 填充悬挂块数据结构并且悬挂该任务*/     suspend_ptr =%26;amp;suspend_block;     suspend_ptr -> qu_queue=queue;     suspend_ptr -> qu_suspend_link.cs_next=NU_NULL;     suspend_ptr -> qu_suspend_link.cs_previous=NU_NULL;     suspend_ptr -> qu_message_area=               (UNSIGNED_PTR) message;     suspend_ptr -> qu_message_size=size;     task=(TC_TCB *) TCT_Current_Thread();     suspend_ptr -> qu_suspended_task=task;     /* 判断该消息队列的等待方式是先进先出还是按任务    的优先级 */     if (queue -> qu_fifo_suspend) {       /* 是先进先出等待方式,将悬挂块链入消息队列      的等待链表 */       CSC_Place_On_List((CS_NODE **)           %26;amp;(queue -> qu_suspension_list),           %26;amp;(suspend_ptr -> qu_suspend_link));     }     else {       /* 按优先级方式将悬挂块链入任务等待链表的      合适位置 */       suspend_ptr -> qu_suspend_link.cs_priority =               TCC_Task_Priority(task);       CSC_Priority_Place_On_List((CS_NODE **)             %26;amp;(queue -> qu_suspension_list),             %26;amp;(suspend_ptr -> qu_suspend_link));     }     /* 悬挂调用任务,并自动取消该消息队列的临界区    保护 */     TCC_Suspend_Task((NU_TASK *) task,             NU_QUEUE_SUSPEND,             QUC_Cleanup, suspend_ptr, suspend);     /* 获取该系统调用要求的返回状态以及返回值*/     status =suspend_ptr -> qu_return_status;     *actual_size =suspend_ptr -> qu_actual_size;     }     else     /* 在消息队列为空以及不等待的方式下,返回状态    指示消息队列为空*/     status =NU_QUEUE_EMPTY; }   这段代码是处理消息队列中没有消息时的情况的,并且在不进行悬挂等待时返回码是NU_QUEUE_EMPTY,提示队列为空。我们注意到在选择悬挂等待的情况下,填充了suspend_ptr指针所指的一个悬挂块结构,suspend_ptr -> qu_message_area填充的是接收任务指定的接收缓冲区指针,suspend_ptr -> qu_message_size填充的是接收任务指定的接收消息长度。接下来依据不同的等待策略(任务优先级或FIFO),将填充好的消息队列悬挂块链入该消息队列的悬挂等待链表中,进行任务调度。正是有了这个消息队列悬挂块数据结构,将来发送消息的任务依据这个悬挂块中指定的接收消息缓冲区指针,把消息从发送任务直接复制到接收任务。当接收消息的任务被唤醒并获得执行权后,只是简单地依据悬挂块中的相关域的内容返回系统调用而已。从上述分析可以看出,悬挂块数据结构起着重要的作用,它不仅标明了是哪个任务在等待,也标明了等待任务的一些详细信息,同时也有结果状态域。通过对Nucleus内核定时器机制的分析得知,在任务等待资源超时的情况下,悬挂等待块的结果状态域将被填充NU_TIMEOUT。 [b] 2 针对Small RTOS51消息队列的分析[/b]   有了上述三种模型的分析,很容易看出Small RTOS51V1.12.1版消息队列所采用的是第2种模型,只是在实现时出现重大遗漏,被唤醒的任务没有竞争到资源时应重新进入等待表,而其内核代码却没有体现到这一点。这一点《Small RTOS51中消息队列的一处隐患》的作者已经分析得很清楚,其提出的第1种解决方案也很正确。重点是第2种解决方案。第2种解决方案属于第3种模型,但其实现技术欠佳。正如原文作者所指出的那样,第2种方案具有其自身不可调和的矛盾:“在发送消息的OSQIntPost()函数中,如果检测到有任务正在等待此消息,则并不把消息数(buf\[0\])加1”,但这个消息毕竟进入消息队列了,这就造成了一种矛盾状态,消息数与消息队列中的实际消息不相符。为了实现第3种模型的效果,即被唤醒的等待任务获取资源,在消息数为0的情况下,原文作者通过进一步判断该任务是否还处在消息队列的等待任务表中,来决定该任务是否从消息队列中获取消息;但消息数为0而消息队列中还有消息却为发送消息带来隐患。要想解决这一矛盾,OSQIntPost()在唤醒等待任务的同时就应该将该消息传递给这个任务,这样消息数仍然为0才不留隐患。uC/OS?II实现这一策略的技术是任务被唤醒后检查任务控制块中的 OSTCBCur->OSTCBMsg这一数据域\[4,5\],获取到的消息指针在此。注意,OSQPost()在有等待任务的情况下,如下处理:  if (pevent->OSEventGrp != 0x00) { /* 判断是否有任务悬挂在消息队列的等待表中            */28OS_EventTaskRdy(pevent, msg,OS_STAT_Q); /*将等待表中最高优先级任务唤醒*/     OS_EXIT_CRITICAL();     OS_Sched(); /* 进行任务调度,运行最高优先级任务*/     return (OS_NO_ERR);   }   即消息指针没有进消息队列并且消息指针通过OS_EventTaskRdy(pevent, msg, OS_STAT_Q)传给被唤醒的任务。这一作法符合第3种模型。   由此可见,Small RTOS51V1.12.1要想实现第3种模型,其内核的数据结构需要有一些变化,像原文第2种方案那样修改代码,是不能最终解决问题的。同 Nucleus相比,实现消息队列时,uC/OS?II虽然没有引入悬挂等待块的概念,但其通过在任务控制块中引入相应数据项来最终实现第3种模型,并且结果是在任务被唤醒后进行判断的。 3 结论   虽然各种各样的实时嵌入式操作系统千差万别,但从操作系统理论的角度分析,很容易将它们纳入到某一具体的模型;实现细节有很大的不同,但其实现的功能应符合通用原理。在操作系统理论的指导下,结合具体的实例源代码分析、理解和应用,才能有更大的把握。                  参考文献 1 陈皓. Small RTOS51中消息队列的一处隐患. 单片机与嵌入式系统应用,2005(7) 2 Jim Mauro,Richard McDougall.Solaris内核结构.北京:机械工业出版社,2001 3 孔祥营,等. 嵌入式实时操作系统VxWorks及其开发环境Tornado. 北京:中国电力出版社,2001 4 Labrosse Jean J.uC/OS?II——源码公开的实时嵌入式操作系统.北京:中国电力出版社,2001 5 Labrosse Jean J.嵌入式实时操作系统uC/OS?II.北京:北京航空航天大学出版社,2003 韩明峰:硕士,主要研究方向为实时嵌入式系统。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭