嵌入式Linux下的实时性增强方案

发布者:alpha11最新更新时间:2010-09-18 来源: 电子技术应用关键字:嵌入式Linux  实时性  中断  可抢占性  自旋锁 手机看文章 扫描二维码
随时随地手机看文章

    Linux以其功能强大、源代码开放、支持多种硬件平台、模块化设计方案以及丰富的开发工具支持等特点广泛应用在嵌入式系统领域。作为嵌入式产品的操作系统平台,具有较好的实时性、系统可靠性、任务处理随机性是系统追求的目标,目前商业嵌入式操作系统实时性能可以满足嵌入式领域的需求,但由于其价格昂贵,应用受到了限制[1]。而嵌入式Linux以其非常低廉的价格,可以大大地降低成本,逐渐成为嵌入式操作系统的首选。但由于其在实时应用领域的技术障碍,要应用在嵌入式领域,还必须对Linux内核作必要的改进。本文以S3C2410+Linux作为移动机器人操作平台,为了提高机器人任务处理的实时性,针对影响Linux OS实时性能的若干方面进行研究,并利用相应的解决方法基于标准Linux2.6内核加以实现,最后通过测试,验证了此改进方法的效果。

1 Linux内核实时性分析

1.1 Linux内核制约实时性的因素

    衡量操作系统实时性的指标主要有中断延迟和抢占延迟。嵌入式系统中很多实时任务是靠中断驱动的,中断事件必须在限定的时限内处理,否则将产生灾难性的后果。大多数实时系统都是处理一些周期性的或非周期性的重复事件,事件产生的频度就确定了任务的执行时限,因此每次事件发生时,相应的处理任务必须及时响应处理,否则将无法满足时限[2]。抢占延迟就反映了系统的响应及时程度。针对Linux内核,中断关闭及中断优先级执行机制、内核不可抢占性、自旋锁(spinlock)及大内核锁及一些O(n)的任务调度算法影响了系统的实时性能。

1.2 现存增强Linux内核实时性的技术

    多年来,Linux实时性改进技术的发展主要有两种技术方案:(1)直接修改Linux内核。针对内核数据结构、调度函数、中断方式进行改动,重新设计一个由优先级驱动的实时调度器,替换原有Linux内核中的进程调度器sched.c。这一方案主要是针对中断机制、任务调度算法进行改进的,较为成功的案例为Kansas大学开发的Kurt-Linux。Kurt提高了Linux系统中的实时精度,将时钟芯片设置为单触发状态。对于实时任务的调度,Kurt-Linux采用基于时间的静态实时CPU调度算法。实时任务在设计阶段就需要明确地说明其实时事件要发生的时间。这种调度算法对于那些循环执行的任务能够取得较好的调度效果;(2)在Linux内核之外进行实时性扩展,添加一个实时内核。实时内核接管硬件所有中断,并依据是否为实时任务给予响应。Fsm Labs公司开发的RTLinux就是依据这种策略开发设计的[3]。以上论述的两种技术方案有其可借鉴之处,但如果综合考虑任务响应、内核可抢占性、实时调度策略等都将影响操作系统的实时性能,因此,这两种技术还不能很好地满足实时性要求。为了增强嵌入式Linux实时性能,下面将介绍中断机制、内核的抢占性以及大内核锁等相关问题。

2 Linux实时性改进方法

 Linux2.4及以前版本内核是不可抢占的,在Linux2.6中,内核已经可以抢占,实时性有所增强。但是内核中仍然有不可抢占的区域,如自旋锁spinlock保护的临界区等。另外,影响内核实时性能的因素还有中断运行机制、大内核锁机制以及调度算法等。

2.1 中断运行机制改进

 在Linux标准内核中,中断是最高优先级的执行单元,硬件架构决定了硬件中断到来的时候在该中断没有被屏蔽的条件下必须处理。不管内核当时处理什么,即便是Linux中最高优先级的实时进程,只要有中断发生,系统将立即响应该事件并执行相应的中断处理程序,这就大大削弱了Linux的实时性能。特别是系统有严重的网络或I/O负载时,中断将非常频繁,实时任务将很难有机会运行,这对于Linux的实时应用来说是不可接受的。Linux采用的关中断技术在关中断区域使相应实时任务得不到响应,增加了实时任务的中断延迟。Linux实时化后自旋锁变为互斥锁的技术,但由于自旋锁的中断处理不能及时响应,降低了系统的实时性能。因此,借鉴Ingo Molnar实时补丁的实时化方法,采用中断线程化技术改进中断运行机制,中断将作为内核线程运行而且赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级,这样,实时任务就可以作为最高优先级的执行单元来运行了,即使在严重负载下仍有实时性保证。另一方面,中断处理线程也可以因为在内核同步中得不到锁而挂载到锁的等待队列中,很多关中断就不必真正的禁止硬件中断了,而是禁止内核进程抢占,从而减小了中断延迟[4]。

 在初始化阶段,常规中断初始化和中断线程化的初始化在start_kernel( )函数中都调用trap_init( )和init_IRQ( )两个函数来初始化irq_desc_t结构体,区别主要体现在内核初始化创建init线程时,中断线程化的中断在init( )函数中还将调用init_hardirqs(kernel/irq/manage.c)来为每一个IRQ创建一个内核线程,最高实时优先级为50,依次类推直到25。因此,任何IRQ线程的最低实时优先级为25,具体实现是通过kthread_create函数创建的。功能实现等同于如下代码:

   void __init init_hardirqs(void)
   {   ……
        for (i = 0; i < NR_IRQS; i++) {  
                          //对于每一个中断建立一个中断线程
        irq_desc_t *desc = irq_desc + i;
            if(desc->action && !(desc->status & IRQ_NODELAY))
              //有IRQ_NODELAY标志的中断不允许线程化
                desc->thread = kthread_create(do_irqd,
                    desc, "IRQ %d", irq);     //建立线程
            ……
        }
    }
    static int do_irqd(void * __desc)
                          //分配中断线程优先级50~25
    {  ……
        /*Scale irq thread priorities from prio 50 to prio 25 */
        param.sched_priority = curr_irq_prio;
        if (param.sched_priority > 25)
        curr_irq_prio = param.sched_priority - 1;
       ……
    }

 在中断处理阶段当中断发生时,CPU调用do_IRQ( )函数来处理中断,do_IRQ( )在做了必要的相关处理之后调用_do_IRQ( )。_do_IRQ( )主要功能为判断该中断是否已经被线程化(核对终端描述符的状态字段是否包含IRQ_NODELAY标志),对于没有线程化的中断,将直接调用handle_IRQ_event( )函数来处理。功能实现等同于如下代码:

   fastcall notrace unsigned int __do_IRQ(unsigned int irq,
        struct pt_regs *regs)
   {  ……
        if (redirect_hardirq(desc))
                //检测是否为线程化中断,若是则唤醒中断线程
                goto out_no_end;
    ……
    action_ret = handle_IRQ_event(irq, regs, action);
                                  //处理非线程化中断
           ……
   }
   int redirect_hardirq(struct irq_desc *desc)
                 //检测irq_desc结构体,判断是否线程化
   {  ……
        if (!hardirq_preemption || (desc->status & IRQ_
                    NODELAY) || !desc->thread)
            return 0;
       ……
        if (desc->thread && desc->thread->state != TASK_
                RUNNING)
            wake_up_process(desc->thread);
        ……
   }

 针对已线程化的情况,调用wake_up_process( )函数唤醒中断处理线程执行,内核线程将调用do_hardirq( )来处理相应的中断。具体实现是通过handle_IRQ_event( )函数直接调用相应的中断处理函数完成的。对于紧急的中断(如时钟中断),内核保持原来的中断处理方式,而不为其创建中断线程,这样就保证了紧急中断的快速响应。

2.2 内核可抢占性设计

 在Linux标准内核中,因不具有可抢占性和导致较大的延迟,增加内核的可抢占性能,可提高系统的实时任务处理能力。当前修改Linux内核提高实时性的方法主要有增加抢占点和改造成抢占式内核两种方法。增加抢占点方法是在内核中插入抢占点,通过检测抢占点调度标志来决定是否进行实时任务的调度。采用这种方法,在检测抢占点标志时大大增加了系统开销,因此本方案采用直接改造Linux内核的方法,通过修改自旋锁为互斥锁来提高内核的可抢占性[5]。即借鉴Ingo Molnar的实时补丁的实时化方法,使用mutex互斥锁来替换spinlock自旋锁。使用mutex替换spinlock,可以让spinlock可抢占。起初spinlock不可抢占性设计目的是避免死锁,可抢占性设计可能导致竞争者与保持者的死锁局面。中断处理函数中也可以使用spinlock,如果spinlock已经被某一进程保持,则中断处理函数无法进行,从而形成死锁。中断线程化以后,中断线程将挂在等待队列上并放弃CPU让别的线程或进程来运行,让每个spinlock都有一个等待队列,该等待队列按进程或线程优先级排队,如果一个进程或线程竞争的spinlock已经被另一个线程保持,它将把自己挂在该spinlock的优先级化的等待队列上,然后发生调度把CPU让给别的进程或线程。mutex替换spinlock后,spinlock结构定义如下代码:

    typedef struct {
        struct rt_mutex lock;            //新的实时互斥锁
        unsigned int break_lock;
    } spinlock_t;
    其中struct rt_mutex结构如下:
    struct rt_mutex {
        raw_spinlock_t wait_lock;
        struct plist wait_list;                  //优先级等待队列
        struct task_struct *owner;     //拥有该锁进程的信息
        int owner_prio;
        … …
    };

  在如上代码中,类型raw_spinlock_t就是原来的spinlock_t。即代码中的spinlock_t就是新设计的自旋锁。rt_mutex结构中,wait_list字段为优先级等待队列。在mutex使用中,当遇到锁住的临界资源时,任务被挂起到wait_list中,临界资源解锁时等待任务被激活。临界资源被保护的同时可以抢占。

    由于Linux内核底层的临界资源是不可抢占的,使用mutex替换spinlock的过程中,这部分可以保留,仍由不可抢占的spinlock保护,如:保护硬件寄存器的锁、调度器的运行队列锁等。不可抢占的spinlock被重新命名为raw_spinlock_t。spin_lock被宏定义为:

 #define spin_lock(lock)  PICK_OP(raw_spinlock_t,spin,_lock,lock)

 函数PICK_OP支持两种锁共存机制,PICK_OP在编译阶段将锁操作转化为mutex或者spinlock:

   #define PICK_OP(type, optype, op, lock)
          do {
          if (TYPE_EQUAL((lock), type))
          _raw_##optype##op((type *)(lock));
          else if (TYPE_EQUAL(lock, spinlock_t))
          //调用gcc的内嵌函数__builtin_types_compatible_p()
      _spin##op((spinlock_t *)(lock));
          else __bad_spinlock_type();
          } while (0)
         #define TYPE_EQUAL(lock, type)
    __builtin_types_compatible_p(typeof(lock), type *)

  gcc的内嵌函数__builtin_types_compatible_p用于判断一个变量的类型是否为某指定的类型,如果类型为spinlock_t,将运行函数_spin_lock;类型为raw_spinlock_t,将运行函数_raw_spin_lock。

   实时rt_mutex在具体应用中,一个高优先级任务抢占该锁的同时,把先前的锁拥有者添加到互斥锁等待队列中,并在当前拥有该锁的任务task_struct中标记等待该锁的所有任务;反之,不能得到该锁就把当前任务添加到锁的优先级等待队列中,直到唤醒执行。为了防止优先级逆转,可以改变锁的当前拥有者的优先级为锁的等待队列中任务的最高优先级。

 rt_mutex可以使高优先级任务利用抢占锁进入临界区,这样内核不可抢占区的数量和范围大大缩小,内核可抢占性有了很大的提高,且降低了实时高优先级任务的抢占延迟,改善了系统的实时性能。

2.3 可抢占大内核锁设计

 大内核锁BKL(Big Kernel Lock)实质上也是spinlock,它用于保护整个内核,该锁保持时间较长,对系统的实时性能影响很大[6]。采用Ingo Molnar的实时化方法,BKL使用semaphore实现,结构定义如下代码:

         struct semaphore {
         atomic_t count;
         struct rt_mutex lock;              //实时互斥锁的使用
        };

   由结构体发现,在BKL实现中利用了实时互斥锁rt_mutex,在改进后的spinlock结构体spinlock_t中也利用了实时互斥锁rt_mutex,因此可抢占大内核锁和新的spinlock共用了低层的处理代码。使用semaphore之后,大内核锁就可抢占了。

3 内核实时性测试

   针对Linux2.6内核,本文并没有作出对内核调度算法的修正,只是探讨了中断运行机制、自旋锁及大内核锁技术在系统实时性能上的局限性,所以实验测试主要测试中断延迟时间和任务响应时间。实验环境: Intel 2 GHz CPU,256 DDR内存,Kernel 2.6.22版本。测试结果如表1所示。

    由表可知,在中断服务程序中写入标记,测试中断触发至中断服务程序执行平均响应时间,标准Linux2.6内核平均中断响应时间为182 μs,改进后Linux2.6内核为14 μs。采用开源软件LMbench3.0 测试系统任务调度延迟时间,标准Linux2.6内核平均任务响应时间为1 260 μs,改进后Linux2.6内核为162μs。由此可见,改进策略在一定程度上大大减小了中断延迟和任务调度时间,有利于改善移动机器人任务处理的实时性能。

 本文基于Linux2.6内核的关中断、中断优先级、内核的不可抢占性以及大内核锁保持时间过长等问题进行了实时性分析,提出了相应的改进方法。利用中断线程化、互斥锁的应用及大内核锁的改进等技术提高了系统的实时性能,降低了内核中断延迟和调度延迟。改进后的内核在移动机器人控制器平台中有很好的应用价值,提高了机器人控制的实时性能。

关键字:嵌入式Linux  实时性  中断  可抢占性  自旋锁 引用地址:嵌入式Linux下的实时性增强方案

上一篇:风河VxWorks MILS Platform平台全面升级
下一篇:基于VxWorks的多路高速串口的通信方法设计

推荐阅读最新更新时间:2024-05-02 21:09

stm32库函数学习篇---NVIC与外部中断
实现功能:外部中断线0(PA0)与线15(PA15)分别连接到两个按钮,一个按钮用于产生上升沿,另一个用于产生下降沿,两个中断函数里均对PA8口连接的led灯取反,同时PD2连接的led灯随意延时取反指示程序运行。 这次我用了官方提供的例程来构建自己的工程,这样可以省下不少时间,而且减少了出错率,调试起来容易多了。 首先是stm32中断与外部中断的概念。 ARM Coetex-M3内核共支持256个中断,其中16个内部中断,240个外部中断和可编程的256级中断优先级的设置。STM32目前支持的中断共84个(16个内部+68个外部),还有16级可编程的中断优先级的设置,仅使用中断优先级设置8bit中的高4位。 STM32可支持
[单片机]
stm32库函数学习篇---NVIC与外部<font color='red'>中断</font>
STM32L552开发板——外部中断
上回向大家介绍了如何使用GPIO口的输出功能点亮一盏LED灯,以及使用GPIO口的输入功能读取按键的状态控制LED的闪烁频率,然鹅,获取按键的状态采用的是主循环查询的方法,这有一个弊端,试想当主循环的循环周期非常长的时候,比如夸张点说要1秒,那就得一直按着按键等到程序扫描到按键的输入引脚后才能放开,这个等待时间最长需要1秒,这是很糟糕的,我可没有这个耐心去等,比如在将一个数从1调节到100的时候,那后果是可想而知的。为此,就有必要引入一个叫中断的神奇东西,程序中的中断,会把正在运行的普通程序或者低级的中断服务程序打断,先运行本中断服务函数内的程序,就好比我们日常生活中的突发事件,你正在洗衣服,突然快递小哥到你家门口了,你得放下手上
[单片机]
STM32L552开发板——外部<font color='red'>中断</font>
查询主从机中断同步的方法
while(!flag)若中断标志flag不为1,意味着还没有响应中断,于是一直不停查询 响应中断之后,flag会变为1,退出循环 然后立即将flag清0,接着马上跳转到goto语句指定的地方,用这个来同步。 即:在约定的时间内进行不断查询,查询的单位时间很短,放弃了一个短时间的效果显示来实现长时间的信号同步 过程描述如下: 上电后,主机,从机各自跑相同的效果,由于晶振的原因,会导致执行速度不同。在短时间内这样的速度差异难以被人察觉,但是这样的差异性是一个累加的过程,在运行了很久之后,各个不同的机器之间的差异就很明显了。 因此我们要在差异累加到不能忽略之前,对每个机器进行同步化处理。现在只是一条连线,从主机的IO口到各个从机
[单片机]
STM32学习笔记——外部中断的初步了解
STM32F103有76个中断,包括16个内核中断和60个可屏蔽中断,具有16级16级可编程的中断优先级。 理解 STM32 的中断,要从中断优先级哦分组开始。而理解中断优先级分组,就要理解什么是抢占优先级,什么是响应优先级。 所谓: 抢占优先级:如果有两中断先后出发,已经在执行的中断优先级如果没有后出发的中断优先级高,就会先处理抢占优先级高的中断,也就是说有较高的抢占优先级的中断可以打断抢占优先级较低的中断,这是实现中断嵌套的基础。 响应优先级:只在同一抢占优先级的中断同时触发时起作用。抢占优先级相同,则优先执行响应优先级较高的中断。响应优先级不会造成中断的嵌套,如果中断的两个优先级都一致,那么优先执行位于中断向量表中位置较高的中
[单片机]
STM32学习笔记——外部<font color='red'>中断</font>的初步了解
风河推出经CC EAL4+安全认证的嵌入式Linux平台
全球嵌入式及移动应用软件领导厂商风河公司(Wind River)今天宣布推出通过CC EAL4+安全认证的Wind River Linux Secure平台,这是市场上第一套通过EAL4+通用标准评估保证等级认证的商用嵌入式Linux平台;该认证经由美国国家信息安全保证联盟(National Information Assurance Partnership,NIAP) 依据《通用作业系统保护规范(GP-OSPP)》予以验证并核准。 针对那些必须达到严格的安全标准和加密技术规范的机构组织,Wind River Linux Secure提供了一套高安全性的商用现货(COTS)嵌入式Linux解决方案,其安全标准及加密技术规
[嵌入式]
STML8的中断使用小结
STM8L的中断,有两种模式,分别为Pin模式和Port模式,也就是所谓线中断和端口中断。 简而言之,线中断是指所有寄存器组的同一管脚Pin的中断都使用同一中断号, 端口中断是指同一个或多个寄存器组的所有管径Pin的中断使用同一中断号。 举例而言 所有Port A/B/C/D/E的Pin1的中断都是EXTI1, Port B 和Port G的所有Pin的中断都是EXTIB/G 实现方面,因为关联寄存器很多,直接操作比较太繁琐,容易出错。 比较好的选择是利用ST的库函数,代码清晰,易于维护,可以参照以下步骤. 简而言之,线中断比较简单,需要依次调用: 1. GPIO_Init() 端口初始
[单片机]
基于嵌入式Linux 系统的高速设备驱动程序实现
1 Linux驱动程序的研究现状 嵌入式系统已越来越广泛应用于通信领域。而Linux操作系统因为其内核小、开源以及可灵活裁剪等优点,在嵌人式设备中得到了广泛的应用。下面首先介绍嵌入式linux系统驱动程序的一般结构。 1.1 传统的驱动程序结构简介 Linux操作系统最基本的组成部分包括资源管理器、调度程序、介于硬件和应用软件之间的接口、网络管理器和文档系统管理器。本文主要阐述介于硬件和应用软件之间的接口——设备驱动程序的实现。 对于多数字符设备而言,其功能主要是数据的传输。驱动程序操作的一般流程是:当read()函数被系统调用时,首先对中断寄存器进行配置,并开中断,并进入中断等待函数。此时系统会调用schedule()函
[单片机]
基于<font color='red'>嵌入式Linux</font> 系统的高速设备驱动程序实现
C51编程17-中断篇(串行通讯1)
在实际的工作中,通常不是一个CPU将所有的事情处理完。为了提高工作效率,通常都是多个CPU之间的合作完成某一项功能。 计算机与键盘之间的数据交换,键盘的所有案件并不是直接连接到计算机的CPU中,而是由键盘中CPU将键盘按键信息处理完后,通过USB-HID的通讯方式上传计算机的CPU。这样子的处理方式计算机(主控)CPU任务处理,更有利于设备更换(不至于键盘坏了,需要换一台计算机)。 计算机之间的基本数据通讯方式有并行通讯与串行通讯两种。 并行通讯:数据各位同时进行传输(发送或接收)的通信方式,数据有多少位,就需要多少根线。 串行通讯:数据的各位一位一位顺序传送的通讯方式,数据传送线少,相对于并行输送速率低。 按
[单片机]
C51编程17-<font color='red'>中断</font>篇(串行通讯1)
小广播
最新嵌入式文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved