应用中的嵌入式Linux实时优化
Linux提供了一些机制让我们得以计算函数的执行时间,gettimefoday()函数是其中之一。函数的原型及需要使用的一个数据结构如下:
int gettimeofday(struct timeval *tv,struct timezone *tz);
strut timeval{
long tv_sec; //second
long tv_usec;//microsecond};
其中,gettimeofday()将当前时间保存在tv结构中,tz一般不需要用到,可用NULL代替。使用示例如下:
main()
{ struct timeval start_time,end_time;
float time_uesd;
gettimeofday(start_time,NULL);
function_in_critical_setion();
gettimeofday(end_time,NULL);
time_used =1000000 (start_time.tv_sec-end_time.tv_sec)+(start_time.tv_usec-end_time.tv_usec);
time_used/=1000000;
exit(0);}
如此即可得出进程在临界区function_in_critical_section()所耗费的时间,以供参考。将Hz值设定在2000,此时系统时钟中断周期为0.5ms,精度提高了20倍。
如图1、图2所示,当进程进入临界区之前,它比较自身的平均执行时间T(NP)和T(REMAIN)的值,当T(NP)≤T(REMAIN)的时候,进程才被允许进入临界区,否则进程进入工作队列等待下一次判断。

本文尝试用数学方法来分析采用这种机制对实时性能的提高。首先给出一个定义:当预定在时刻t时执行的实时任务推迟到时刻t'时才执行,则t'-t称作系统延迟,用Lat(OS)表示。在普通Linux中,Lat(OS)如下:
Lat(OS)=T(NP)+ T(SHED)
设任意时刻 ,T(NP)≤T(REMAIN)的机率为ρ,则普通Linux中的平均Lat(OS)为
AvLat(OS)=ρ[T(NP)+ T(SHED)] +(1-ρ)[T(NP)+ 2T(SHED)]
引入前述机制后,由于总是优先保证实时任务的执行,Lat(RT-OS)固定式为:
Lat(RT-OS)=T(SHED)
采用该机制前后系统廷迟的变化为
δ=AvLat(NOR-OS)-Lat(RT-OS)=T(NP)+(2-ρ)T(SHED)
在一个特定系统里,ρ是固定的,而在Linux 2.6中,采用O(1)算法后T(SHED)也是固定的,由前式可得出结论:在临界区的进程执行时间长的系统中,引入该机制前后平均系统廷迟下降的越大,系统实时性能的改善越明显。
3.2 优先级量顶
试描述一个如下场景:低优先级的任务L和高优先级H任务需要占用同一共享资源,低优先级任务开始后不久,高优先级任务也准备就绪,发现所需共享资源被占用后,任务H被挂起,等待任务L结束释放该资源。此时一个不需要该资源的中优先级任务M 出现,调度器依据优先原则转而执行任务M。这就进一步廷长了任务H的等待时间,如图3所示。更加恶劣的情况是,如果出现了更多的类似任务M0,M1,M2,...,将有可能使任务H错过临界期限(Critical Deadline),而导致系统崩溃。
在一个不太复杂的实时系统中,可采用优先级置顶的方法解决这一问题。该方案对每一个可能被共享的资源分配一个优先级,该优先级为有可能使用这个资源的最高优先级的进程的优先级(如下伪代码中的RESOURCE_X_PRIO)。由调度器将优先级传给使用该资源的进程,进程结束后其自身的优先级(如下伪代码中的TASK_A_PRIO)才恢复正常。这样就避免了上面场景中任务L被任务M抢占,而导致任务H始终处于挂起状态。优先级置顶的示例代码如下:
void task_a(void)
{……
set_task_priority(RESOURCE_X_PRIO);
…… //Accessing shared resource x
set_task_priority(TASK_A_PRIO);……}
3.3 内核线程
中断服务程序(ISR)是不能被抢占的。一旦CPU 开始执行ISR,除非程序结束,否则不可能转而执行其他的任务。Linux用自旋锁(Spinlock)来实现ISR对CPU的独占。采用了自旋锁的ISR是不能进入休眠的,而且此时系统的中断也被完全禁止。内核线程是由内核创建和撤销的,用来执行一个指定的函数。内核线程具有自己的内核堆栈,能够被单独调用。我们用内核线程代替ISR,并且用互斥量(Mutex)替换自旋锁。内核线程能够进入休眠,而且执行时是不禁用外部中断的。系统接到中断信号后,唤醒相应的内核线程,内核线程代替原来的ISR执行完任务后继续进入休眠状态。这样中断廷时就是可预测的,并且占用时间也很少。
根据LynuxWorks公司的测试数据,在Pentium III 1GHz的PC上,Linux 2.4内核的平均任务响应时间为1133us,平均中断响应时间为252us;而Linux 2.6内核的平均响应时间为132us,平均中断响应时间仅为14us,比Linux 2.4内核提高了一个数量级。在此基础上,采用这种方法能够针对具体的系统进一步加快特定中断的响应时间,提高应用系统的实时性能。
4 总结与展望
本文以Linux 2.6为基础探讨了提高Linux实时性的方法。引入了在实时系统中,只有当进入临界区的进程能在下一个实时任务开始之前结束时才被允许执行的机制,保证实时任务总是优先得到执行;采用了优先级置顶的方法避免了出现优先级倒置的情况;用内核线程代替中断服务程序,改变了了一般中断服务程序执行中不能进入休眠状态的情况,并且执行时不禁用外部中断,使系统的中断廷时变得短小和可预测。本文所述方法的缺点在于,提高系统时钟中断频率带会增大系统开销问题。为了在实时性能提升和系统开销增大之间找到一个平衡点,开发者不得不对具体系统做大量测试,具体问题具体分析,使得该方法在适用性上打了折扣。Linux因其免费、性能强大、工具众多的特点,必将在嵌入式系统领域得到大量的应用。我们应该及时跟踪国内外Linux发展动态,同时积累在此领域的开发经验,走出自己的路来。
评论