深入探讨《Small RTOS51中消息队列的一处隐患》
发布时间:2007/8/29 0:00:00 访问次数:488
摘要: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/OSII等来讲,都没有采用这种策略。
第2种,将该资源等待队列中的一个任务唤醒,依据所采用的策略不同,可以是等待任务中优先级最高的,也可以是第1个进入等待队列中的任务。这个任务被唤醒后将和系统中的其他任务一起竞争这个资源。如果这个任务最终没有竞争到这个资源,它将再次进入该资源的等待队列并进行任务调度。
第3种,将该资源等待队列中的一个任务唤醒,依据所采用的策略不同,可以是等待任务中优先级最高的,也可以是第1个进入等待队列中的任务,这点和第2种方法是一样的。和第2种情况不同的是,这个任务被指定为资源的获得者。主流实时内核VxWorks、Nucleus、uC/OSII等都采用这种策略。以VxWorks为例,其内核文档指出[3]:“任务或ISR调用msgQSend()向消息队列发送消息。此时如果没有任务在等待该队列中的消息,那么该消息进入消息队列的缓冲;如果有任务等待该队列的消息,那么这个消息立即提交给第1个等待的任务。”这段话有两方面的含义:① 明确指出第1个等待的任务获得资源;② 第1个等待的任务获得资源的方式是直接从消息的发送者那里获得,也就是说这个消息将不进入消息队列进行缓冲,消息在发送者和接收者之间进行手把手的传递。对于这种机制的实现,可以以著名的源代码公开的实时嵌入式操作系统Nucleus为例。下面是Nucleus内核关于接收消息的一段精彩的代码:
else {
/* 消息队列为空,决定是否悬挂等待*/
if (suspend) {
/* 增加等待该消息队列的任务数量 */
queue -> qu_tasks_waiting++;
/* 填充悬挂块数据结构并且悬挂该任务*/
suspend_ptr =&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 -&g
摘要: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/OSII等来讲,都没有采用这种策略。
第2种,将该资源等待队列中的一个任务唤醒,依据所采用的策略不同,可以是等待任务中优先级最高的,也可以是第1个进入等待队列中的任务。这个任务被唤醒后将和系统中的其他任务一起竞争这个资源。如果这个任务最终没有竞争到这个资源,它将再次进入该资源的等待队列并进行任务调度。
第3种,将该资源等待队列中的一个任务唤醒,依据所采用的策略不同,可以是等待任务中优先级最高的,也可以是第1个进入等待队列中的任务,这点和第2种方法是一样的。和第2种情况不同的是,这个任务被指定为资源的获得者。主流实时内核VxWorks、Nucleus、uC/OSII等都采用这种策略。以VxWorks为例,其内核文档指出[3]:“任务或ISR调用msgQSend()向消息队列发送消息。此时如果没有任务在等待该队列中的消息,那么该消息进入消息队列的缓冲;如果有任务等待该队列的消息,那么这个消息立即提交给第1个等待的任务。”这段话有两方面的含义:① 明确指出第1个等待的任务获得资源;② 第1个等待的任务获得资源的方式是直接从消息的发送者那里获得,也就是说这个消息将不进入消息队列进行缓冲,消息在发送者和接收者之间进行手把手的传递。对于这种机制的实现,可以以著名的源代码公开的实时嵌入式操作系统Nucleus为例。下面是Nucleus内核关于接收消息的一段精彩的代码:
else {
/* 消息队列为空,决定是否悬挂等待*/
if (suspend) {
/* 增加等待该消息队列的任务数量 */
queue -> qu_tasks_waiting++;
/* 填充悬挂块数据结构并且悬挂该任务*/
suspend_ptr =&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 -&g