近年来,基于pc的嵌入式系统得到迅速的发展。在各种不同的操作系统中,由于linux操作系统的廉价、源代码的开放性以及系统的稳定性,使其在基于pc的嵌入式系统中的应用日益广泛。rtlinux(realtime linux)[1]是 一种基于linux的实时操作系统,是由fsmlabs公司(finite state machine labs inc.)推出的与linux操作系统共存的硬实时操作系统。它能够创建精确运行的符合posix.1b标准的实时进程;并且作为一种遵循gpl v2协议的开放软件,可以在gpl v2协议许可范围内自由地、免费地使用、修改和再发行。本文介绍了rtlinux的特点及功能,并结合一个实时处理的具体实例对其编程方法加以说明。
1 rtlinux的特点
在linux操作系统中,调度算法(基于最大吞吐量准则)、设备驱动、不可中断的系统调用、中断屏蔽以及虚拟内存的使用等因素,都会导致系统在 时间上的不可预测性,决定了linux操作系统不能处理硬实时任务。rtlinux为避免这些问题,在linux内核与硬件之间增加了一个虚拟层(通常称 作虚拟机),构筑了一个小的、时间上可预测的、与linux内核分开的实时内核,使得在其中运行的实时进程满足硬实时性。并且rtlinux和linux 构成一个完备的整体,能够完成既包括实时部分又包括非实时部分的复杂任务。
1.1 硬实时性
rtlinux将linux源码中所有的cli、sti、iret指令分别用宏s_cli、s_sti、 s_iret替换,引入的虚拟层将截取所有的硬件中断,分割linux系统与硬件中断之间的直接联系。当rtlinux虚拟层接收到与实时处理有关的硬件 中断时,立即启动执行相应的实时中断服务程序;而接收到与实时处理无关的中断时,先保存相应的信息,等到rtlinux内核空闲时通过软中断传递给 linux内核去处理,这样就使得rtlinux内核不受各种软、硬件中断的影响,不会造成时间上的不可预测性。同时又区别于其他的实时处理方案,它并未 对操作系统的内核作结构性的修改,因此并不会妨碍linux操作系统的进一步发展和变化。
linux采用基于最大吞吐量准则的调度策略,并不能确保各个实时进程的及时调度。而rtlinux在缺省情况下采用优先级的调度策略,即系统 调度器根据各个实时任务的优先级来确定执行的先后次序。优先级高的先执行,优先级低的后执行,这样就保证了实时进程的迅速调度。同时rtlinux也支持 其它的调度策略,如最短时限最先调度(edp)、确定周期调度(rm)(周期短的实时任务具有高的优先级)。rtlinux将任务调度器本身设计成一个可 装载的内核模块,用户可以根据自己的实际需要,编写适合自己的调度算法。
操作系统精确的定时机制,可以提高任务调度器的效率,但增加了cpu处理定时中断的时间开销。rtlinux采用一种折衷的方案,不将8354 定时器设计成10毫秒产生一次定时中断的固定模式,而是根据最近事件(进程)的时间需要,不断调整定时器的定时间隔。这样既可以提供高精度的时间值,又避 免过多增加cpu处理定时中断的时间开销。rtlinux系统同时将各时间间隔相加,保持一个系统全局时间变量,并使用软中断的方式来模拟传统的 100hz定时中断,将其传递给linux系统使用。
1.2 完备性
过去,实时操作系统仅是一组原始的、简单的可执行程序,它所做的仅仅是向应用程序提供一个程序库。但如今,实时应用程序通常要求能够支持 tcp/ip、图形显示、文件和数据库系统及其它复杂的服务。为了满足当今实时应用程序的多种需求,通常采用在实时控制内核上增加这些服务或完全修改标准 操作系统内核的方法,而rtlinux所采用的是一种新型高效的方式。将一个简单的小型实时内核与linux内核共存,用简单的小型实时内核处理实时任 务,将非实时任务交给linux内核去处理,而linux内核本身也作为一个rtlinux实时内核在空闲时运行的进程。这种将实时系统和平均时间优化的 标准linux操作系统协同工作的方式,使得许多实时应用都显示出一种增效。实时内核中的实时任务可以直接访问硬件,不使用虚拟内存,给实时进程提供了很 大的灵活性;运行在linux用户空间中的非实时任务,可以方便地使用系统提供的各种资源(网络、文件系统等),并受到系统的保护,增加了系统的安全 性。
2 rtlinux的主要功能
rtlinux提供了一整套对硬实时进程的支持函数集。在此,仅对在嵌入式系统中最重要的三个方面:进程间的通讯、中断和硬件设备的访问以及线程间的同步加以阐述。
2.1 进程间的通信(ipc)
rtlinux要求将应用程序分成实时部分和非实时部分。应用程序的实时部分应该是简单的和轻负荷的,在rtlinux的实时内核中完成;而非 实时部分,在linux的用户空间完成。因此rtlinux提过了多种内核实时进程和linux用户空间进程间的通讯机制,最重要的是实时fifo和共享 内存。
实时fifo是能够被内核实时进程和linux用户空间进程访问的快进快出队列,是一种单向的通讯机制,可以通过两路实时fifo构成双向的数据交换方式。在使用实时fifo前先要对实时fifo通道初始化:
在初始化实时fifo通道后,rtlinux内核的实时进程和linux用户空间的进程都可以使用标准的posix函数open、read、 write和close等对实时fifo通道进行访问。内核实时进程还可以使用rtlinux的专有函数rtf_put和rtf_get对实时fifo通 道进行读写。
rtlinux共享内存由mbuff.o模块支持,可以使用下面的函数分配和释放共享内存块:
#include
void *mbuff_alloc(const char *name, int size)
void mbuff_free(const char *name, void *mbuf)
函数mbuff_alloc有两个参数,共享内存名name和共享内存块的大小size。如果指定的内存共享名并不存在,分配成功时返回共享内 存指针,访问计数置为1,分配失败时返回空指针;如果指定的内存共享名已经存在,返回该块共享内存的指针,并将访问计数值直接加1。函数 mbuff_free将该块共享内存的访问计数值减1,当计数值为0时,该共享内存被释放。在实时内核模块中使用该函数时,应该将函数 mbuff_alloc和 mbuff_free分别放在init_module 和cleanup_module模块之中。
2.2 中断和访问硬件
硬中断(实时中断)具有最低的延时,在系统内核中只有少数的实时进程使用。函数rtl_request_irq和rtl_free_irq用于安装和卸载指定硬件中断的中断服务程序。
#include
int rtl_request_irq(unsigned int irq, unsigned int
(*handler) (unsigned int, struct pt_regs *))
int rtl_free_irq(unsigned int irq)
中断驱动的线程可以使用唤醒和挂起函数:
int pthread_wakeup_np(pthread_t thread)
int pthread_suspend_np(void)
一个中断驱动的线程可以调用函数pthread_suspend_np(pthread_self())阻塞自身线程的执行,然后由中断服务函 数调用函数pthread_wakeup_np唤醒该线程的执行,直到此线程再次调用函数 pthread_suspend_np(pthread_self())将自身挂起。
软中断是linux内核常常使用的中断,它能够更安全地调用系统函数。无论如何,对于许多任务来说并不能提供硬实时性能,将会导致一定的延时。
int rtl_get_soft_irq(void (*handler)(int, void*, struct pt_regs *), const char* devname)分配一个虚中断并安装中断处理函数;void rtl_global_pend_irq(int ix) 激活虚中断;void rtl_free_soft_irq(unsigned int irq) 释放分配的虚中断。
rtlinux与linux一样通过/dev/mem设备访问物理内存,具体由模块 rtl_posixio.o 提供此项功能。首先应用程序应该打开/dev/mem设备,通过函数mmap对某段物理内存进行映射后,即可使用映射后的地址访问该段物理内存。应用程序 只能在linux进程中(即在应用程序的init_module()模块中)调用mmap,在实时进程中调用mmap将会失败。另一种访问物理内存的方法 是通过linux的函数ioremap[2]。rtlinux 访问i/o端口的函数如下(对于x86结构):
输出一个字节到端口:
#include
void outb(unsigned int value, unsigned short port)
void outb_p(unsigned int value, unsigned short port)
输出一个字到端口:
#include
void outw(unsigned int value, unsigned short port)
void outw_p(unsigned int value, unsigned short port)
从端口读一个字节:
#include
char inb(unsigned short port)
char inb_p(unsigned short port)
从端口读一个字:
#include
short inw(unsigned short port)
short inw_p(unsigned short port)
其中带后缀_p的函数使读写端口时有一个小的延时,这在快速的计算机访问慢速的isa设备时是必需的。
2.3 线程同步
当多个实时线程需要访问共享资源时,如果没有一种同步机制,将破坏共享资源中数据的完整性。rtlinux提供一种简单的加锁方法mutex来 控制对共享资源的存取,并支持posix的pthread_mutex_ family函数组[3]。目前有以下函数可以使用:
pthread_mutexattr_getpshared //得到指定属性线程共享属性值;
pthread_mutexattr_setpshared //设置指定属性线程共享属性值;
pthread_mutexattr_init //初始化mutex的属性;
pthread_mutexattr_destroy //删除mutex的属性;
pthread_mutexattr_settype //设置mutex信号的类型;
pthread_mutexattr_gettype //得到mutex信号的类型;
pthread_mutex_init //按指定的属性初始化mutex;
pthread_mutex_destroy //删除给定的mutex;
pthread_mutex_lock //锁定mutex,如果mutex已被锁定,阻塞当前线程直到解锁;
pthread_mutex_trylock //锁定mutex,如果mutex已被锁定,函数立即返回;
pthread_mutex_unlock //解锁mutex;
互斥信号类型有pthread_mutex_normal (default posix mutexes)和pthread_mutex_spinlock (spinlocks)
3 rtlinux的编程实例分析
下面结合一个具体的程序parport.c[4],对rtlinux的编程特点加以说明。程序parport.c中的实时线程在并口的2、3脚 (并口的数据d0和d1)上周期输出信号1,而对应硬件中断7的实时中断服务程序将在并口的2、3脚输出信号0。连接并口的2脚和10脚(并口的确认信号 线,对应于计算机的硬件中断7),则可在并口的2、3脚上产生一个方波信号。parport.c源程序如下:
#include
#include
#include
#include
#include
#include
pthread_t thread;
unsigned int intr_handler(unsigned int irq,struct pt_regs *regs){//中断服务函数
outb(0, 0x378); //输出字节0到并口数据线
rtl_hard_enable_irq(7); //使能硬件中断7
return 0;
}
void * start_routine(void *arg){ //实时线程
struct sched_param p; //定义实时线程控制参数的数据结构
p. sched_priority = 1; //设置优先级为1
pthread_setschedparam (pthread_self(), sched_fifo, &p);//设置实时线程的控制参数
pthread_make_periodic_np(pthread_self(),gethrtime(),100000);//启动周期为10ns的实时线程
while (1){
pthread_wait_np(); //实时线程挂起
outb(3, 0x378); //实时线程周期执行,输出3到并口数据线
}
return 0;
}
int init_module(void) {//初始化模块
int status;
rtl_irqstate_t f;
rtl_no_interrupts(f); //保存当前的中断状态标志到变量f,并禁止中断
status=rtl_request_irq(7, intr_handler); //设置硬件中断7的处理程序
rtl_printf(′rtl_request_irq: %dn′, status); //输出的控制台
outb_p(inb_p(0x37a) | 0x10, 0x37a); //使能并口中断(硬件上)
rtl_hard_enable_irq(7);//使能中断7(软件上)
rtl_restore_interrupts(f); //按照变量f恢复当前的中断状态标志,并使能中断
return pthread_create (&thread, null, start_routine, 0);//创建实时进程thread
}
void cleanup_module(void) { //清除模块
rtl_free_irq(7); //禁止中断7
pthread_delete_np (thread); //删除实时进程thread
}
程序parport.c的make文件如下:
all: parport.o
include rtl.mk
clean:
rm -f *.o
按照如下命令对程序进行编译:
make
运行程序可采用以下命令:
modprobe rtl_sched //调入所需的处理模块
insmod parport.o //调入parport.o模块
连接并口的2脚和10脚,即可通过示波器在并口的3脚上观测到输出的方波信号。
可以看到,rtlinux的实时程序被编写成可加载的linux内核模块,它能被动态地加入内存,不能执行linux系统调用,模块的初始化代码对实时任务的结构作初始化,把实时任务的时限、周期和释放时间等实时参数传递给rtlinux。
通过对linux最小的改动,提供一种可靠且廉价的硬实时操作系统rtlinux。rtlinux开发者可以充分利用linux提供的各种方便 来编写任务的非实时部分,加速自己的任务进度。
LED开路保护器在LED灯串中的应用解析
新讯发机器视觉检测解决方案:标签正反检测
静电放电问题典型案例分析
吉时利2450型触摸屏数字源表的使用优势和典型应用分析
响应教育部新政策,维视智造新工科人才培养方案助力院校创新体系建设
操作系统RTLinux的主要功能、特点及应用分析
如何达到IP68?智能手机气密性防水检测方案
多功能调料盒将会成为厨房必不可少的工具之一
人工智能与机器人技术发展方向辨析
Linux中的符号链接如何创建
中日两国引领市场 外骨骼机器人仍然受到高成本的困扰
什么是单稳态触发器_单稳态触发器特点以及构成
三星携S9强势回MWC 小米正式亮相MWC展会
利用设计平台将原型开发周期缩短六个月
链云宝:四大智慧场景服务综合平台
人脸识别技术发展迅速,导致出现滥用现象
百万高清摄像机的清晰度与哪些器件参数有关
一种基于Zigbee的路灯控制系统实现方案
智能照明控制系统的设计原则
2.8英寸TFT触摸屏的使用教程