摘要:实时操作系统要求具有速度快和可预测性的特点,必须保证实时任务在要求的时间内完成。本文在分析Linux操作系统的中断方式和进程调度等影响实时性的因素后,对Linux的实时性策略进行改进,提出了双内核解决方案。 关键词: Linux 实时性 调度 双内核
1 前言 实时系统可以定义为"一个能够在事先指定或确定的时间内完成系统功能和对外部或内部、同步或异步事件作出响应的系统"。实时操作系统 (Realtime OS)是实时系统中使用的操作系统。实时操作系统的任务不只是要求完成每一个工作,并且要按照给定的时限按时完成每一个工作,所以实时操作系统必须能够确保其任务对时间的要求。 实时有硬实时和软实时之分。硬实时和软实时的区别就在于对外界的事件做出反应的时间。硬实时系统必须是及时对事件做出反应,绝对不能错过事件处理的deadline情况。在硬实时系统中如果出现了这样的情况就意味着巨大的损失和灾难。比如说核电站中的堆芯温度控制系统,如果没有对堆芯过热做出及时的处理,后果不堪想象。软实时系统是指,如果在系统负荷较重的时候,允许发生错过deadline的情况而且不会造成太大的危害。比如说程控电话系统允许在105个电话中有一个接不通。 现有的Linux是一个通用的操作系统,它是按照分时系统的目标设计的,进程调度强调平衡各进程之间的响应时间来保证公平的CPU时间占用。虽然采用了许多技术来加快系统运行和反应速度,但它本质上不是一个实时操作系统。为了保证其实时性,必须采用一定的策略加以改进。
2 影响Linux实时性能的主要因素 在Linux系统中,响应中断的过程如下:设备产生一个中断、中断处理函数开始执行、唤醒任务并在运行队列排队、任务获取CPU并开始执行、任务执行完毕。图1为事件处理过程各个阶段图以及影响响应时间的因素(每个阶段下面的字母A~J表示这个阶段中影响响应时间的因素): 由图中可以分析出导致Linux系统不能满足实时系统短的响应时间和确定的执行行为的要求的主要因素有以下方面: (1)Linux操作系统存在关中断的机制。导致的结果是:如果低优先级的进程由于进入临界区或者为了尽快完成任务而关闭了中断,那么即使有高优先级实时进程的中断发生系统也无法响应。这种情况在实时系统中是不允许发生的。 (2)Linux操作系统内核是禁止抢占的。一个进程一旦进入内核,它将运行直到系统调用结束或进程被阻塞。这时候一个高优先级的实时进程只能等待。这样的设计是为了简化任务调度,但对实时进程来说这样的时间等待是不允许的。
(3)Linux使用的是基于优先级的任务调度策略。这种调度策略不能保证实时任务按时完成。Linux虽然给实时进程提供了较高的优先级,但是,并没有加入时间限制。例如完成的最后期限、应在多长时间内完成、执行周期等等。例如,Linux的基于时间片的调度策略可能使得一个实时进程在一个时间片内未完成,其优先级将降低,从而可能造成到截止时间实时任务无法完成。 (4)其他方面 Linux利用交换空间让进程运行在一个比实际内存大的虚拟内存空间中。当进程访问的虚拟内存的内容在交换空间里时,Linux就要把在交换空间里的页面交换到实际的内存中,而这段时间是不可预测的,这造成了实时响应时间的不确定性; 在Linux中,高优先级的进程不能抢占低优先级进程的资源。即如果高优先级的进程要使用低优先级进程正在使用的资源时,它必须等待低优先级的进程释放资源,这样容易产生优先级倒置; 另外,Linux的周期模式定时器频率仅为100Hz,远不能满足多种实时应用的要求。 Linux之所以有以上问题,是因为它最主要的设计原则是最大限度的利用各种资源,力求最公平的调度各个进程,以获得最大的整体性能,这也正是通用操作系统的设计原则。
3 Linux关中断机制 Linux内核可以看成是一个不断对请求进行响应的服务器,这些请求可能来自正在cpu上执行的进程,也可能来自正在执行中断请求的外部设备。因此,内核的各个部分并不是严格按照顺序依次执行的,而是采用交错执行的方式。内核控制路径是指内核用来处理系统调用、异常或中断所执行的指令序列。在交错执行内核控制路径时,要避免可能带来的数据混乱的危险,因此引入了临界区的概念。临界区是指一段代码,在其他的内核控制路径能够进入临界区前,进入临界区的每一内核控制路径都必须全部执行完这段代码。 另外,在Linux操作系统中,大部分外部中断都是开启的,中断处理一般由设备驱动程序来完成。由于通用操作系统中的用户进程一般都没有实时性要求,而中断处理程序直接跟硬件设备交互,可能有实时性要求,因此中断处理程序的优先级被设定为高于任何用户进程。 但对于实时操作系统采用上述的中断处理机制是不合适的。外部中断是环境向实时操作系统进行的输入,它的频度是与环境变化的速率相关的,而与实时操作系统无关。如果外部中断产生的频度不可预测,则一个实时任务在运行时被中断处理程序阻塞的时间开销也是不可预测的,从而使任务的实时性得不到保证。因此,Linux内核的进程经常关闭中断以尽快完成自己的任务。但同时也引入了问题:如果低优先级的进程关闭了中断,那么即使有高优先级实时进程的中断发生系统也无法响应。这种情况在实时系统中也是不允许发生的。
4、RTLinux的双内核解决方案 4.1基本思想 针对Linux的关中断机制,RTLinux提出了采用虚拟机技术模拟Linux关中断的解决方法。基本思想如下:Linux不直接与中断控制硬件进行联系,而是在二者之间加入了一个中断控制硬件的仿真层,这个仿真层只提供实时服务,它使得Linux在不能禁止中断的同时,还能对Linux内核的同步需求提供支持。一旦中断到来,就先由该仿真层处理,在仿真层完成了所有需要进行的实时处理后,才会交给Linux进行进一步的处理。如果Linux已经进行了禁止中断的操作,则仿真层只是将该中断标记为处于挂起状态。当Linux进行了允许中断的操作后,仿真层就会将控制切换到处于挂起状态的、具有最高优先级中断的中断处理程序。 4.2工作原理 将Linux操作系统内核作为一个任务执行在一个小的实时操作系统之上。事实上,Linux是这个实时操作系统的空闲的任务,它仅在没有实时任务运行的情况下执行。Linux作为一个任务本身不能禁止中断。解决Linux禁止中断的问题是通过在实时内核中模拟Linux中断例程完成的。当Linux内核执行cli()禁止中断时,一个软中断标志被设置。当一个中断发生时,实时内核将捕获中断并根据这个标志和中断掩码来决定是否将该中断交给Linux内核处理。因此,虽然允许Linux禁止中断,但中断对实时内核总是可见的。如果该中断将引起一个实时任务的执行,则实时内核保存Linux的状态并立即开始执行实时任务。如果不是实时中断,中断就被挂起,之后查看Linux对该中断的操作状态,如果Linux没有禁止此中断,就将该中断交给Linux,由Linux执行相应的中断处理函数。如果Linux禁止中断,则在Linux重新开启中断时,实时内核处理所有挂起的中断交由Linux执行相应的中断处理函数。 4.3具体实现 对于软中断,RT-Linux在初始化模块Init_module()中首先调用结构相关函数arch_takeover() 来覆盖原来的关中断相关的函数,主要有:使用rtl_soft_cli替换__cli;使用rtl_soft_sti替换__sti;使用rtl_soft_save_flags替换__save_flags_ptr;使用rtl_soft_restore_flags替换__restore_flags。 处理中断时,使用rtl_intercept()来代替do_IRQ(),用来执行与一个中断相关的所有中断服务例程。这里主要做的修改是将实时中断与普通Linux中断分开处理。 1)它首先调用函数rtl_irq_controller_get_irq(regs)判断是否获取到中断, 2)如果获取到中断,并且是实时irq请求,就调用dispatch_rtl_handler执行实时irq对应的中断服务例程,然后,从中断返回。 3)如果是非实时irq请求,就挂起irq请求G_PEND(irq),设置挂起标志表明有挂起的irq请求G_SET(g_pend_since_sti)。 若Linux开启了中断,就解除irq挂起状态G_UNPEND(irq),调用函数dispatch_Linux_irq执行该irq对应中断服务程序,之后从中断返回。 当实时进程与普通进程进行通信时使用实时FIFO技术。当insmod将rtl_fifo.o驱动程序插入Linux内核时,该驱动程序将自己注册为RTLinux的一部分,并成为Linux驱动程序。一旦插入Linux内核,用户空间进程和实时任务都可使用实时FIFO,如图2所示。
任何硬实时任务都是在RTLinux的控制下运行的,该任务一般可执行周期性任务、处理中断并与I/O设备驱动程序通信,以采集或输出模拟和数字信息。当实时任务需要告诉用户进程有一个事件将发生时,它便将这一消息送给实时FIFO。每一个FIFO都是在一个方向上传送数据:从实时任务到用户空间,或反之。因此,双向通信需要使用两个FIFO。 下面说明实时FIFO的使用方法: 1)FIFO是在init_module()时调用rtf_create()创建的,在cleanup_module()中调用rtf_destroy()撤销。 2)在创建FIFO时,可以调用rtf_create_handler()注册该实时FIFO的处理程序。每次Linux进程读或写该FIFO时,rtl_fifo驱动程序都要调用该处理程序。 3)对FIFO的写入和读出是通过函数rtf_put()和rtf_get()来实现的。任何读出或写入实时任务一侧的操作都是非模块操作,因此rtf_put()和rtf_get()都立即返回,而不管FIFO状态是什么。 如上例数据采集,可以注册一个FIFO。当有中断表明数据到来时,该中断服务例程可以调用rtf_put()将数据写入FIFO,而Linux进程就可以使用rtf_get()从FIFO中读出数据进行处理。
5、结束语 RTLinux的双内核解决方案还可以结合实时内核与非实时OS内核的综合优势,实时进程与普通Linux进程之间使用实时FIFO传递信息。即可以提高实时系统的可用性,也可以节省计算资源,同时将实时系统的一部分任务划分出来,降低了实时内核需要处理的复杂度,提高了实时的计算效率。
参考文献: 1 陈莉君著,《Linux操作系统内核分析》人民邮电出版社,2000.3 2 郭玉东著,《Linux操作系统结构分析》西安电子科技大学出版社,2002.1 3 William Stallings著,魏迎梅、王涌等译,《操作系统——内核与设计原理》电子工业出版社,2001.6 |