一、 看门狗监控线程
1. 让一个线程去做这个事情(喂狗),而这个线程需要知道其它线程目前的状态(这个应该很容易);这样就做到了用一个狗来监控多个线程或进程的目的.
2. watchdog虽然需要1.6秒喂一次,但是可以做一个中间层,在规定的时间内去喂狗,但应用程序却可以在一个更大的时间内(比如1分钟)去喂一次中间层.
二、一种多线程看门狗监控方法
主要包括:初始化看门狗监控队列并建立看门狗监控进程;创建其他应用线程;如线程中有阻塞,在阻塞前先关闭该线程的看门狗监控并等待阻塞资源有效;如无阻塞则开启该线程的看门狗监控;监控线程等待定时信号,定时检测看门狗监控队列;检测监控对象的开始时间是否达到,未到达则返回等待步骤;检测到监控对象中剩余时间小于0,则判断该线程为异常,回收该线程(或通过硬件看门狗复位系统),返回创建其他应用线程步骤;否则,将该线程执行时间减去T1,返回开启看门狗监控步骤;在功能线程执行一次循环后再次返回。此法是一种有效、可控的看门狗的监控方法。
多线程看门狗监控方法,至少包括以下步骤:步骤一,初始化看门狗监控队列;步骤二,建立看门狗监控进程,将本步骤的监控线程定义为最高优先级,周期执行时间为T1;步骤三,创建其他应用线程,在创建其他应用线程时根据需要设定是否进行看门狗监控;如需要,则设置看门狗监控的开始时间T2和执行时间T3;步骤四,如果线程中有阻塞资源,在阻塞前先关闭该线程的看门狗监控;否则跳至步骤六;步骤五,等待步骤五中所述阻塞资源有效;步骤六,开启该线程的看门狗监控,重置开始时间和执行时间为T2,T3;步骤七,监控线程等待定时信号,定时检测看门狗监控队列;步骤八,检测监控对象的开始时间是否达到,未到达返回步骤七;步骤九,检测到监控对象中剩余时间小于0,则判断该线程为异常,回收该线程(或通过硬件看门狗复位系统),返回步骤三;步骤十,将该线程执行时间减去T1,返回步骤六;步骤十一,在功能线程执行一次循环后,返回步骤四。
三、
高安全性操作系统要求系统在任何环境下安全可靠地运行,需要系统设计工程师对系统资源进行有效配置和管理。本文中将从存储器保护、访问控制和资源可用性保障等几个方面介绍高安全性操作系统的一些设计思想和方法。
高安全性系统的设计极为复杂,通过加强操作系统安全保护和资源保障可以减轻应用开发人员的设计负担。无论设计远程通信交换机还是医疗器械或航天器上的复杂系统,系统中关键部件必须能在任何条件下正常工作。实际上,在处理器速率稳步增长条件下,出于节省成本的考虑,设计工程师往往期望同一处理器能运行多个关键程度不同的应用系统,但是相应的风险也将增加。
例如,重病特别护理使用的血液气体分析仪具有两种截然不同的功能。首先分析仪需要实时监控病人血流中氧气及其它气体的含量。如果某种受监控气体低于或高于某个危险值,分析仪应发出告警或采取某些更为直接的干涉措施。但该设备还具有另一项功能,即显示气体含量的变化曲线,以便于脱机分析。在这样的系统中,数据记录、数据显示和用户接口线程可通过处理器或其它资源的监控和告警线程来实现。
为了使同一系统中不同重要程度的线程“和平共处”,管理处理器和其它资源的操作系统必须能对软件进行适当的分区,以保证资源的有效配置。操作系统必须在此刻加以保障,而不能依赖于设计之后或实现之后的测试。高安全性系统应当在任何时刻都是安全的。
在本文中,线程是程序的基本执行单位;进程是一个由地址空间组成的程序执行单元,通常包含一个或多个线程;内核是操作系统中提供核心系统服务(如调度、线程同步和进程间通信)的部分。
存储器保护
最初的容错设计为存储器保护。多年以来,微处理器一直带有片上存储器管理单元(MMU),MMU能使单个软件线程工作于硬件保护地址空间。但是在许多商用实时操作系统中,即使系统中含有这些硬件也没采用MMU。
当应用程序的所有线程共享同一存储器空间时,任何一个线程将有意或无意地破坏其它线程的代码、数据或堆栈。异常线程甚至可能破坏内核代码或内部数据结构。例如线程中的指针错误就能轻易使整个系统崩溃,或至少导致系统工作异常。
就安全性和可靠性而言,基于进程的实时操作系统(RTOS)的性能更为优越。为生成具有单独地址空间的进程,RTOS只需要生成一些基于RAM的数据结构并使MMU加强对这些数据结构的保护。基本思路是在每个关联转换中“接入”一组新的逻辑地址。MMU利用当前映射,将在指令调用或数据读写过程中使用的逻辑地址映射为存储器物理地址。MMU还标记对非法逻辑地址进行的访问,这些非法逻辑地址并没有映射到任何物理地址。
这些进程虽然增加了利用查询表访问存储器所固有的系统开销,但其实现的效益很高。在进程边界处,疏忽或错误操作将不会出现,用户接口线程中的缺陷并不会导致其它更关键线程的代码或数据遭到破坏。目前在可靠性和安全性要求很高的复杂嵌入式系统中,仍然存在采无存储器保护的操作系统的情况,这实在有些不可思议。
采用MMU还有利于选择性地将页面映射或解映射到逻辑地址空间。物理存储器页面映射至逻辑空间,以保持当前进程的代码,其余页面则用于数据映射。类似地,物理存储器页面通过映射可保持进程的线程堆栈。RTOS可以在每个线程堆栈解映射之后,很容易地保留逻辑地址所对应的页面内容。这样,如果任何线程分配的堆栈发生溢出,将产生硬件存储器保护故障,内核将挂起该线程,而不使其破坏位于该地址空间中的其它重要存储器区,如另一线程堆栈。这不仅在线程之间,还在同一地址空间之间增加了存储器保护。
存储器保护(包括这类堆栈溢出检测)在应用程序开发中通常非常有效。采用了存储器保护,程序错误将产生异常并能被立即检测,它由源代码进行跟踪。如果没有存储器保护,程序错误将导致一些细微的难以跟踪的故障。实际上,由于在扁平存储器模型中,RAM通常位于物理地址的零页面,因此甚至NULL指针引用的解除都无法检测到。
系统调用
另一个问题是内核必须保护自己免受不适当的系统调用影响。许多内核将新创建的内核对象的实际指针以句柄(handle)形式返回至创建该对象的线程。当该指针在后续系统调用中传回内核时,将直接解除引用。但是,如果线程使用该指针直接修改内核对象,或者用指向其它存储器的指针简单地覆盖该句柄时,将可能带来严重的后果。
错误的系统调用不应使整个内核崩溃,因此RTOS应对内核对象采用不透明句柄,并验证所有系统调用的参数。
容错和高可用性
即使最优秀的软件也可能存在缺陷。随着应用程序的日益复杂,以及在软件支持不足条件下处理功能的增加,使得现场系统中的缺陷也不断增多。因此,系统设计工程师必须考虑到这些潜在的故障并在应用系统中采用故障恢复技术。当然,故障恢复的效果取决于应用系统本身:故障发生后,用户接口可重新启动,而对于飞行控制系统则可能无法重启。
进行故障恢复的一种方法是为每个地址空间设立一个管理线程。当出现线程故障(如堆栈溢出)时,内核应提供某些机制使故障信息传送至管理线程。如有必要,管理线程通过系统调用关闭该故障线程或整个进程,然后重新启动。管理线程还可挂靠在软件“看门狗”设置中,由此检测线程死锁和空闲问题。
在许多关键系统中,往往利用系统中的多个冗余节点来保障高可用性。在这些系统中,工作于冗余节点上的内核必须能检测出工作节点中的故障。一种方法是在RTOS各处理器之间的消息传递机制中添加内置“心跳信息”(见图1)。一旦系统启动,将打开冗余节点和每个工作节点之间的通信信道。正常工作条件下,冗余节点不断收到工作节点的心跳信息。如果心跳信息无法抵达,冗余节点将自行控制。
强制访问控制和自主访问控制
Unix文件是自主访问控制的一个范例:进程或线程可自行修改文件的权限,因而允许系统中的另一进程访问该文件。自主访问控制对于某些系统中的特定对象非常适用。
应用于高安全性系统的RTOS必须更进一步,对关键系统对象进行强制访问控制。例如,在由飞行控制程序控制的飞机传感器中,系统设计工程师必须能静态地配置系统,以保证只有飞行控制程序才能操作该设备。系统中的其它应用程序均不能动态请求对该器件进行操作,而且飞行控制程序不能动态地使系统中的任何其它应用程序操作该器件,访问控制还得到了内核的进一步加强。强制访问控制带来了可靠保障,自主访问控制只对使用该方式的应用程序特别有效,而且必须假定这些应用程序存在一些缺陷。
空间域上保障资源的可用性
在高安全性系统中,关键应用程序不能占用全部的存储器资源。在大多数实时操作系统中,存储器通常用于保持线程控制块及其它中央存储区的内核对象。
当一个线程创建新线程、信号灯或其它内核对象时,内核将占用中心存储区中的一块存储器以保持对象的数据。因此,一个线程中的错误将可能导致程序创建过多的内核对象而耗尽所有的中心存储区(见图2a),对于更为关键的线程甚至有可能带来灾难性的后果。
为了确保这种情况不会发生,RTOS可采用存储器配额系统,在该系统中,系统设计工程师静态规定每个进程占有的物理存储器份额(见图2b)。例如,用户接口进程最大可占有128KB,而飞行控制程序为196KB。如果用户接口进程中的某个线程发生了上述错误,接口进程将耗尽所占有的128KB存储器,但飞行控制程序及其分配的196KB存储器则完全不受影响。
在高安全性系统中:当线程请求创建内核对象时,父进程必须提供相应的存储器配额以满足请求,这类空间域的保护应作为RTOS设计的一部分。当需要提供存储器保障时,中央存储区和自主分配限额显然不再够用。
如果RTOS提供存储器配额系统,那么低关键性应用程序可以采用动态加载,而已运行的高关键性应用程序必须提供所需的物理存储器。此外,用来保持任何新进程的存储器应来自创建进程的存储器配额。如果该存储器来自中央存储区,当恶意或无意的写应用程序试图创建太多新进程时,进程创建很可能失败。在大多数高安全性系统中,不用担忧动态进程的创建,而RTOS应当可以重配置以使该功能可从系统中去除。
保证资源的时域可用性
绝大多数RTOS采用了基于优先级的占先调度程序。在这种机制下,系统中优先级最高的线程总是占用处理器。如果多个线程具有同等优先级,这些线程一般通过时间片(timeslicing)占用方式实现对处理器的平均分配。在给定的优先级条件下,时间片占用方式并不能保证处理器时间被关键线程占用。
考虑如下情形:系统包含两个具有同等优先级的线程,线程A是不重要的后台线程,而线程B则是至少需要40%处理器时间才能运行完成的关键线程。由于线程A和线程B分配了同等优先级,典型的调度程序将分割时间片,以保证每个进程都获得50%的处理器时间。此时,线程B能顺利运行完成。现在假定线程A创建了一个具有同等优先级的新线程,这样3个具有最高优先级的进程将共享处理器。线程B由于突然只获得33%的处理器时间,因而无法完成其关键任务。这种情形下,如果线程A的代码本身存在缺陷或受到病毒影响,进程A可能会创建数十个乃至数百个“联合”线程,而使线程B只能占用很少一部分运行时间。
在调度程序中,采用特定优先级条件下某个线程的最大“权重”参数可以解决这个问题(见图3)。当一个线程创建另一具有同等优先级的线程时,该进程必须转让自身的部分权重给新创建的线程。在上例中,假定系统设计工程师预先分配线程A和线程B的权重,使得线程A占用60%的运行时间,而线程B占用40%的运行时间。当线程A创建第三个线程时,必须转让部分权重,在此设为30%。现在线程A和新线程均占用30%的处理器时间,而关键线程B的40%则维持不变。线程A可创建许多联合进程而不影响线程B的运行,由此保证了线程B的处理器资源预留。某些重要的嵌入式系统,尤其是航天系统中需要能提供这类资源可用性的保障,并采用基于标准调度技术的调度程序。
所有调度程序固有的一个问题是调度程序并不了解线程所驻留的进程。还是在上例中,假定线程A在用户接口进程中运行,而关键线程B则在飞行控制进程中运行。这两个应用程序均在空间域而非时域中划分并受保护,高安全性系统的设计工程师需要确保用户接口的运行参数不影响飞行控制系统的运行参数,而线程调度程序并不能做出上述保证。
考虑以下情况:通过使线程B的优先级高于线程A或用户接口中的任何其它线程,使其占用必需的全部运行时间。设计缺陷或不正确测试可能导致线程B实际的优先级降低(实际上所有的内核均有这样的性能),从而使得用户接口的线程控制了处理器。类似地,线程A的优先级提升超过线程B也将产生相同的效果。
为了确保处理器中不同关键程度的线程不互相影响,一种方法是提供进程级的调度程序,这种要求已受到高安全性软件的设计工程师的关注。进程或分区、调度概念是ARINC 653规范的主要部分(ARINC 653 是一个航天应用软件标准接口)。
ARINC 653分区调度程序根据系统设计工程师建立的时序流程运行分区或进程,每个进程在重复的时序流程中提供一个或多个运行窗口。在每个窗口期间,其它进程中的所有线程均不能运行,而只有当前活动的进程中的线程才能运行(通常根据标准线程调度准则进行调度)。当飞行控制应用程序的窗口活动时,可以确保其进程资源,此时用户接口应用程序不能运行,因而无法在该窗口中占用关键应用程序的处理时间。
尽管ARINC 653没有严格规定,但显然更为谨慎的实现方法是采用后台分区。当活动分区中不再有可运行线程时,分区调度程序应能运行后台分区中仍然活动的后台线程,而不是处于空闲状态。低优先级的诊断代理程序就是后台线程的一个实例,该程序间断执行,但没有严格的实时期限。
通过有选择地停止活动区中的所有线程并运行下一分区中的所有线程,即可尝试在现有商用操作系统上添加分区调度处理。这样,分区交换时间是分区中线程数目的线性函数,这显然是令人难以接受的实现方法。RTOS必须在内核中运行分区调度程序,以保证分区切换具有恒定的时间并尽可能快捷。
可调度性
满足严格的时限是实时操作系统的最基本要求之一,对于高安全性系统尤为重要。根据系统和线程要求,错过时限将是非常严重的问题。
系统设计工程师经常利用速率单调分析(RMA)来分析和预测系统的时序行为。在这个过程中,系统设计工程师依靠基本的操作系统提供临时而且快速的确定性系统业务。设计工程师不仅需要了解线程代码执行的时间,还必须确定与线程相关的任何系统开销。系统开销通常包括关联切换时间(运行系统调用所需的时间)以及中断、中断处理程序使能和运行系统开销。
所有的实时操作系统都将承担关联转换引发的系统开销。较短的关联转换时间意味着较小的系统开销、更为有效的进程资源利用,并能更好地满足时限要求。实时操作系统的关联转换代码通常需要经过优化以达到最佳的运行速率。
中断等待时间
典型的嵌入式系统通常需要处理几类来自不同设备的中断。某些中断的优先级较高,需要尽快加以处理。例如,通知内核读取传感器的中断对于航天器飞行控制而言至关重要,因此需要最短的处理等待时间。另一方面,典型的定时器中断频率可为60Hz或100Hz,在硬件中总是采用10毫秒等待时间,因此定时器的中断等待时间一般比其它中断的等待时间要求更为宽松。
在系统调用期间,当内核对内部数据结构进行操作时,大多数内核将使所有的中断失效。中断失效的结果是当内核数据结构发生变化时,定时器中断不会触发(定时器中断可能导致关联转换)。系统的中断等待时间直接与内核中最长临界区的长度相关。
实际上,为了避免低优先级的定时器中断,大多数内核增加了所有中断的等待时间。更好的解决方案是在内核系统调用时不禁止中断,而是将定时器中断的处理推迟至系统调用完成之后。这种策略适用于所有内核系统调用过程中时间较短的情况(或者至少较长时间的调用可以重启),这样调度事件可以先于系统调用完成。因此,重新回到调度程序的时间可通过一些指令加以改变,但改变后的调度时间仍然要比较短并在一定的限制范围之内。以这种方式设计带有可抢先系统调用的内核则要复杂得多,这也正是大多数内核不采用这种方式的原因。
有界执行时间
为了对系统调用中线程占用的系统开销进行计算,RTOS应为这些调用提供有界的执行时间。消息传输时序和互斥量(mutex)时序是涉及的两大难题。
线程需要占用一定的时间完成各项任务,其中最主要的任务是代码执行,其它还包括发送和接收消息。消息传送的时间取决于数据的大小,设计工程师如何计算传送时间呢?RTOS具备这样的功能,即控制传送时间是否归属于发送线程或接收线程,或者两者之间共享。实际上,内核的调度程序应将所有的任务(而不单单是最主要的任务)视为具有优先级顺序的执行单元,这样系统设计工程师就能适当地控制和处理这些任务。
优先级倒置
优先级倒置一直是系统设计工程师尝试速率单调分析(RMA)的绊脚石,因为RMA要求较高优先级的线程在较低优先级线程之前运行。当较高优先级的线程由于获取的互斥量(或二元信号灯)被较低优先级的线程占用,而且较低优先级的线程因为中等优先级的线程也在运行而无法执行或释放该互斥量时,将出现优先级倒置(图4)。最常见的RTOS优先级倒置解决方案是支持优先级继承协议。
支持优先级继承的互斥量工作原理如下:如果高优先级的线程试图占用已被低优先级线程占有的互斥量,内核自动提升低优先级线程为高优先级线程。一旦低优先级线程释放了互斥量,其优先级将恢复正常,然后接着运行高优先级线程。动态优先级提升可防止当高优先级线程仍在等待时运行中等优先级线程,从而避免了优先级倒置(见图5)。在本例中,临界区的执行时间(低优先级保持互斥量的时间)将添加至高优先级线程的系统开销中。
优先级继承协议不足以防止连锁阻塞。假定中等优先级的线程试图获得低优先级占用的互斥量,而根据优先级继承协议,随着低优先级线程的优先级提升至中等,高优先级的线程将开始运行并试图获取另一个已被中等优先级线程占用的互斥量。中等优先级线程的优先级将被提升为高优先级,但高优先级线程现在必须等待低优先级线程和中等优先级线程完成后才能重新运行。
连锁阻塞的临界区可延伸至包含可能访问相同互斥量的任何线程的临界区,这不仅极大地增大了系统设计工程师计算系统开销的难度,而且由于系统设计工程师必须计算最坏情形下的系统开销,连锁阻塞现象可能导致系统效率极低(见图6)。在RMA分析任务中,这些阻塞因素将增加计算时间,并有可能导致系统无法调度。由于连锁阻塞的原因,设计工程师可能需要寻求更快的CPU或者去掉系统的部分功能。
优先级最高限额协议不仅能解决优先级倒置问题,还能防止连锁阻塞(见图7)。在一个实现方案(称为最高“锁定者”)中,每个信号灯均有关联优先级,该优先级由系统设计工程师指定为最高优先级线程的优先级。当一个线程获得该信号灯后,将立即提升至该信号灯的优先级。随着信号灯的释放,该线程也将恢复原来的优先级。由于优先级的提升,在信号灯释放之前,其它可能争夺信号灯的线程将不能运行。显然这有效地抑制了连锁阻塞。
多种RTOS既能支持优先级继承,又能支持优先级最高限额,这完全取决于系统设计工程师的选择。
系统要求的改变
当前使用的许多实时操作系统最初专为那些简单小型、运行于不带存储器保护硬件的处理器上的软件系统而设计。随着嵌入式系统中应用程序复杂度的不断增加,容错和高可用性特性正越来越重要,尤其是高安全性系统中必不可少。
容错最初是从进程和存储器保护开始发展,但已经扩展至更多的领域,尤其是需要在时域和空间域保证资源可用性的那些领域。像优先级最高限额协议这样的内核支持特性使得高安全性系统的设计工程师能在系统设计中极大地提高效率,并保障系统调度。
作者:David Kleidermacher
工程总监
email: [email protected]。
Mark Griglock
技术经理
email: [email protected]
Green Hills软件公司