μClinux下实时任务的一种实现方法
摘要:文中以ARM7 TDMI处理器为例,阐述了μClinux进程调度的原理。根据该原理,提出了一种在μClinux平台下实现实时任务的设计方法,这种方法较简单,易于实现。这种设计一般可应用于工业控制领域,可以解决一些工控环境中对连续执行任务的实时要求。
关键词:嵌入式系统,实时任务,μClinux,中断,任务调度
1. 引 言
随着嵌入式系统的不断发展,它在工业控制领域也得到越来越广泛的应用。为了在嵌入式系统上实现复杂的软件功能,比较好的方法是先构建操作系统(让操作系统完成与硬件打交道的工作),然后在操作系统上编写应用软件。目前非常流行的μClinux是一种很好的嵌入式操作系统,它既是开放源代码,又是免费的。μClinux是从原来PC平台上的Linux操作系统改进而来的,继承了Linux高效、稳定的特性,所以在很多场合得到应用。
然而,μClinux应用于工控领域有一个很大的障碍。在大多数工控环境中对于时间的要求是很严格的,几近苛刻。而μClinux本身并没有关注实时问题,它并不是为了Linux的实时性而提出的,所以迫切要解决的问题就是针对特定的应用设计其它的方法,使μClinux能够完成相应的实时任务。
本文从作者所参与的项目开发实例入手,具体阐述在μClinux下设计和完成一种实时任务的具体思想和方法。这种设计可以在一定程度上解决实时问题。
2. μClinux简介
μClinux是一种优秀的嵌入式Linux版本。μClinux是micro-control-linux的缩写,意思是针对微控制领域而设计的Linux系统。同标准Linux相比,它集成了标准Linux操作系统的稳定性、强大网络功能和出色的文件系统等主要优点。没有MMU是μClinux与标准Linux的基本差异。
标准Linux是针对有MMU的处理器设计的。在这种处理器上,虚拟地址被送到MMU,把虚拟地址映射为物理地址。通过赋予每个任务不同的虚拟-物理地址转换映射,支持不同任务之间的保护。
对μClinux来说,其设计针对没有MMU的处理器。不过,仍然采用存储器的分页管理,在加载应用程序时程序分页加载。但是由于没有MMU管理,所以实际上μClinux采用实存储器管理策略。操作系统对内存空间没有保护,各个进程实际上共享一个运行空间。一个进程在执行前,系统必须为进程分配足够的连续地址空间,然后全部载入主存储器的连续空间中。
μClinux操作系统主要有三个基本部分组成:引导程序、μClinux内核(由内存管理、进程管理和中断处理等构成)和文件系统。μClinux可以通过定制裁减内核,使之小型化,还可以加上图形用户界面 (GUI)和应用程序,并将其放在ROM、RAM、FLASH或Disk On Chip中启动。由于嵌入式μClinux操作系统的内核定制高度灵活性,开发者可以很容易地对其进行按需配置,来满足实际应用需要。
3. 项目概述
作者现在正参与一个电子提花机嵌入式系统设计的项目。提花机是一种控制纺织机织出花纹图案的设备,它由机械和电子两部分组成。框图如下:

用来织布的花样文件从USB口输入,存储在FLASH上的。通过传感器和织机发出的信号,ARM核心板将花样文件的数据传向磁铁板,控制织机织出花样来。考虑到有文件系统和显示的方面的要求,所以采用μClinux作为其操作系统。
提花机在运行的过程中有一个步骤对于实时性的要求是相当高的,就是快车运行。快车是指高速的连续输出花样文件数据,织机不停歇的不断运转的过程。这对软件设计提出较高的要求,在这个过程中不能收到其他进程的干扰,否则织机的动作就会出错。
而μClinux是一个分时的操作系统,不能有一个用户进程独占CPU的运行时间。每个进程都只有系统分配的一段时间可以运行,这样不能满足提花机快车运行的任务要求。所以为了改进软件设计,我们先来看一下μClinux进程调度的原理。
4. μClinux进程调度原理
μClinux沿用了Linux对于进程管理的传统,将CPU的时间分片使用。系统内部有定时器,每隔一定时间会产生一次时钟中断。然后挂起所有的进程,执行中断服务。在时钟中断返回的地方执行调度函数。(μClinux(ARM no mmu为例)返回到用户空间前的部分代码)
ENTRY(ret_to_user)
ret_slow_syscall:
ldr r1, [tsk, #TSK_NEED_RESCHED]
ldr r2, [tsk, #TSK_SIGPENDING]
1: teq r1, #0 @ need_resched => schedule()
bne reschedule
… …
reschedule:
bl SYMBOL_NAME(schedule)
阴影部分就是中断返回时调用调度函数schedule()的语句。在schedule()函数中,调用函数goodness()计算执行队列runqueue(就绪的进程队列)中的每一个进程的综合权值,再根据这个综合权值来决定队列中可运行的进程,让其占有CPU的下一个时间片。
既然如此,所有在用户空间运行的进程都会受到时钟中断的打扰。即使在用户空间没有其它的用户进程在运行,但是不能避免在内核空间有其它的“例行公事”的进程,这样实时任务就会受到影响,可能无法在规定的时间上完成。
5. 解决方案
在Linux环境下,如果进程进入了内核空间,内核就无法剥夺其运行权。因为调度进程是在返回用户空间前夕进行的,所以可以将快车程序编入内核,让它成为内核进程,来保证它独占CPU。最简单成为内核进程的方法是将它作为驱动程序来运行。
但是,如果作为驱动程序,那就不能向它传递参数了,这对于许多应用来说是一种障碍。在本项目中,需要输出的花样文件的内存地址和织机的各种参数,都需要传递给快车程序,所以不能传递参数是一个相当大的限制。
但是这个问题仍然可以解决。由于采用的是自己定制的嵌入式系统,我们在设计硬件电路时,加入小型的EEPROM用来存放参数信息,在程序运行的过程中,向EEPROM填入参数的信息。到快车开始的时候,驱动程序自己去读入那些参数,这样就可以了。
6. 具体实现
快车程序比较复杂,在这里就不赘述了。具体说一下μClinux下驱动程序的编写方法。μClinux2.0内核不支持内核模块动态加载,设备驱动及文件系统需要静态地编译进内核。
首先在函数fast_init()和fast_isr_init()中向内核注册设备和中断处理程序。
Void fast_init()
{
int result;
fast_isr_init();
result=register_chrdev(40,”fastrun”,&fastrun_fops); /* 主设备号为40,设备名为fastrun,fastrun_fops是设备接口函数的结构。*/
… …
}
void fast_isr_init()
{
request_irq(40,fastrun,,SA_INTERRUPT,”fastrun”,NULL); /*中断号为40,处理函数为fastrun()*/
… …
}
然后要完成编译链接。将编写好fastrun.c的驱动程序文件复制到uclinux/linux/driver/char下,在μClinux的字符设备初始化函数chr_dev_init()中加入快车的初始化函数fast_init(),并且修改该目录下的Makefile文件L_OBJS+=fastrun.o, 即可将fastrun的设备驱动编译进内核。再在uClinux/mmfs/dev目录下新增设备文件节点,重新编译μClinux内核后就完成了。
1. 结 论
本文描述了一种在μClinux平台下完成实时任务的方法。这种方法简单有效,不仅限于作者所讨论的应用,还可以用于其他要求连续执行任务的场合,能够满足在嵌入式平台上一般工控任务的实时需求。
参考文献
[1] 毛德操,胡希明著.《LINUX内核源代码情景分析》2001年9月
[2] Alessandro Rubini & Jonathan Corbet 《LINUX设备驱动程序》2002年11月
[3] 杜春雷编著《ARM体系结构与编程》2003年3月
[4] 陆宝转,邵贝贝,李荐民 uClinux的设备驱动程序开发――《单片机与嵌入式系统》2003年4期
万加富,张占松,闫荷花 uClinux系统分析――《计算机与现代化》 2003.10期
Posted on December 2nd, 2008 by HENRY
Filed under: xn--8prq9q.com | edit