一种中断下半部设计方案
扫描二维码
随时随地手机看文章
μC/OS-II 是一种基于优先级的抢占式多任务实时操作系统,包含了实时内核、任务管理、时间管理、任务间通信同步(信号量,邮箱,消息 队列)和内存管理等功能。它可以使各个任务独立工作,互不干涉,很容易实现准时而且无误执行,使实时应用程序的设计和扩展变得容易,使应用程序的设计过程大为减化。μC /OS-II是一个完整的、可移植、可固化、可裁剪的占先式实时多任务内核。μC/OS-II绝大部分的代码是用ANSI的C语言编写的,包含一小部分汇编代码,使之可供不同架构的微处理器使用。至今,从8位到64位,μC/OS-II已在超过40种不同架构上的微处理器上运行。μC/OS-II已经在世界范围内得到广泛应用,包括很多领域, 如手机、路由器、集线器、不间断电源、飞行器、医疗设备及工业控制上。实际上,μC/OS-II已经通过了非常严格的测试,并且得到了美国航空管 理局(Federal Aviation Administration)的认证,可以用在飞行器上。这说明μC/OS-II是稳定可靠的,可用于与人性命攸关的安全紧要(safety critical)系统。除此以外,μC/OS-II 的鲜明特点就是源码公开,便于移植和维护。
但是对于中断处理,即使是最新版本的μC/0S—IIv2.85也未能提供层次化的处理机制来帮助开发者开发更灵活高效的中断服务程序。中断随时可能发生,而中断服务程序执行的越快越好。因此很多优秀操作系统均实现了分层次处理中断响应的机制来达到这个目的。多任务系统中,内核负责管理各个任务,或者说为每个任务分配CPU 时间,并且负责任务之间的通讯。内核提供的基本服务是任务切换。μC/OS-II可以管理多达64个任务。由于它的作者占用和保留了8个任务,所以留给用户应用程序最多可有56个任务。赋予各个任务的优先级必须是不相同的。这意味着μC/OS-II不支持时间片轮转调度法(round-robin scheduli ng)。μC/OS-II为每个任务设置独立的堆栈空间,可以快速实现任务切换 。μC/OS-II近似地每时每刻总是让优先级最高的就绪任务处于运行状态,为了保证这一点,它在调用系统API 函数、中断结束、定时中断结束时总是执行调度算法,μC/OS-II通过事先计算好数据简化了运算量,通过精心设计就绪表结构使得延时可预知。
简而言之,层次化的中断处理机制实质上是一种可以使开发者在处理中断时尽量缩短屏蔽中断时间,提高异步事件响应的机制。μC/OS—II的设计者Jean J.Labrosse在其着作中谈到:实时内核最重要的指标就是中断关了多长时间。所以为了帮助开发者合理设计中断服务程序,以使其嵌入式系统获得最好的实时性,μC/OS—II有必要实现层次化的中断处理机制。
1 中断下半部的设计方案
目前,在μC/OS—II内核中任务的运行空间分为中断空间和任务空间。中断空间即为中断服务程序运行所处的空间,这时处理器执行中断服务程序,而所有任务(task)都被处于被中断态。对很多处理器而言,在中断空间内中断请求是被屏蔽的。中断下半部的引入将中断空间一分为二,如图1所示。中断的上半部为中断服务程序,执行那些有严格时限要求不能被打断的工作;中断的下半部执行那些在中断上半部被延后,允许被中断的工作。
μC/0S—II已经设计了完善的中断服务程序的入口和出口函数,所以为了实现中断上半部和下半部的衔接,要从μC/0S—II的中断出口函数OSintExit()着手进行修改。修改后的OSIntExit()实现的功能将是:退出中断的上半部,检查中断下半部是否有就绪的服务程序,若有则在允许中断的情况下执行中断下半部的处理函数,若没有则进行任务调度恢复处理器到任务空间执行。
中断下半部的核心是中断下半部的管理函数OSDo-Sirq()。它的功能是检查中断下半部的状态变量,依据优先级顺序选择就绪的下半部服务程序顺序执行,并且对相应的下半部状态进行修改,最后跳转到下半部的出口函数。出口函数OSSirqExit()使处理器完成从中断空间到任务空间的转换。
本设计使用softirq来指中断下半部的服务程序。中断下半部支持最多32个具有不同静态优先级的softirq。中断优先级范围为0~31,O是最高优先级,31是最低优先级。这里引入了优先级的思想,因为上半部服务程序可能需要对应的softirq来完成延后的任务,类似的softirq也应有不同的优先级来标识它们先后的运行顺序。而采用静态实现的目的是为了实时性和稳定性的考虑,若采用动态实现,代价是可能产生内存碎片和更多的处理器资源损耗。
2 中断下半部的实现
基于操作系统设计全局的考虑,中断下半部的实现应遵循以下几点原则:
①中断下半部也将运行于中断空间,这意味着任务空间的所有任务都要被阻塞。中断下半部与中断上半部(即中断服务程序)一个根本的不同是:中断下半部允许中断。
②尽量对原μC/0S—II体系结构做最小化的修改,如任务调度机制、任务空间的各种保护和同步机制等。改动所涉及的范围越大,引入bug的可能性也越大。在所增加的代码中尽量利用原μC/0S—II提供的系统调用,如开关中断还有任务调度等函数,这样兼顾了效率和安全性。
③尽量减少使用平台相关性代码,保证μC/OS—II的可移植性。
④设计简洁明确的API接口,以方便其他开发者能够轻松使用这种机制。
根据中断下半部的设计方案,其实现分为以下4个主要的模块。
2.1 中断下半部入口的实现
μC/OS—II核心代码os_core.c中的OSIntExit()函数是μC/OS—II中断处理程序的出口。为了实现中断下半部的入口,应将OSIntExit()函数中if((OSIntNesting=0)&&(OSLockNesting==O))语句以下列代码来代替:
第1条if语句判断是否所有中断服务程序都已经结束,注意这里也包括softirq。因为在进入下半部管理函数后会执行OSIntNesting++,若softirq正在执行则OSInt-Nesting一定大于O。这个简单的if判断语句消除了soft—irq的重入的可能性。判断条件为真后,继续判断全局变量softirq_flag,若其值为SOFTIRQ_ENABLE则启用中断下半部。全局变量softirq_stat可能的值有3个:
①SOFTIRQ_READY,说明有就绪的softirq等待运行;
②SOFTIRQ_RUNNING,说明softirq正在被调度但其状态可能为被中断态;
③SOFTIRQ_NONE,说明没有softirq处于就绪状态。
此判断语句条件为真时,函数OSIntCallSirq()将会保存被中断任务的上下文,初始化中断下半部堆栈指针,并执行下半部管理函数OSDo-Sirq()。若判断结果为假,则中断处理返回被中断的语句继续执行。而这条语句可能为中断下半部的代码,也可能为任务空间的代码。0S—IntCallSirq()是一段具有平台相关性的汇编代码,在不同的处理器平台上有不同的实现代码,其流程如图2所示。
2.2 下半部管理函数OSDoSirq()的实现
这是中断下半部实现的核心部分。其代码如下:
首先,通过使用OSIntNesting++以防止softirq的重入,设置softirq_stat的值为S0FTIRQ_RUNNING以标识softirq在执行。通过检查softirq_pending的值来判断是否还有就绪的softirq等待执行。
然后,利用INTS_0N()显示允许中断,并执行getHighPrioSirq()函数快速地判断已就绪最高优先级的softirq的序号。getHighPrioSirq()利用了PendingMap[]数组实现了以空间换时间的思想,能够快速计算出一个32位无符号整数中最低一位“1”的序号。PendingMap口是有256个INT32U类型数据的数组,PendingMap[temp]的值就是以二进制表示的8位无符号整数temp中最低一位“1”的序号。getHighPrioSirq()判断一个32位整型无符号数中最低一位“1”的序号,最多只要经过4次与操作和移位操作。
softirq[]是中断下半部服务函数指针数组,它内含32个数据对应不同的32个softirq。(*softirq[num])()会将PC设为第num个服务函数的入口地址,从而执行这个服务函数。
当所有的就绪softirq执行完成后,设置softirq_stat为SOFTIRQ_NONE,执行OSIntNesting一一,并调度下半部出口函数OSSirqExit()离开中断下半部。
2.3 中断下半部出口函数OSSirqExit()的实现
OSSirqExit()将首先判断OSLockNesting的值,若为O,则执行OSStartHighRdy()调度执行已就绪的最高优先级的任务;若非0,则执行OSResumeCur()调度执行被中断的任务,如图3所示。
2.4 通过API使用中断下半部
本设计的中断下半部提供了以下API,供开发者使用这种机制:
这个调用将使当前任务阻塞并立即切换到中断下半部执行softirq的系统调用。开发者可能希望在开启中断并且禁止任务调度的情况下执行某个任务(利用softirq,这很容易做到),并且对实时响应外部中断无任何影响。
以上API接口均经过良好的设计,功能定义明确,实现代码短小精悍,所有带返回值的函数在遇到参数错误的情况下,能返回相应的错误信息以有利于开发者调试。
3 测试中断下半部对实时性的贡献
3.1 测试平台及测试方法说明
ARM7没有MMU,ARM720T是MMU的 ,ARM9主要包括ARM9TDMI和ARM9E-S等系列ARM9是有MMU的,ARM940T只有Memory protection unit.不是一个完整的MMU。 测试采用三星公司基于ARM7核的S3C44BOX处理器,其工作在66 MHz的频率。μC/OS—II版本号为2.85。用处理器内部的定时器在调试环境下进行时间测试,因为调试环境下可以通过设置断点,快速、准确地查看定时器的当前值。
测试方法:使用中断下半部对一个中断服务的典型应用进行修改,分别测试修改前和修改后的中断响应中关闭中断的时间,并对比关闭中断时间来说明实时性。
3.2 测试中断下半部屏蔽中断的时间
主要函数屏蔽中断的时间如表1所列。
从表1可以看出,在中断下半部入口函数OSIntExit()中所增加的代码给内核增加了约6.2μs的关中断时间,中断下半部管理函数OSDoSirq()给内核增加了约3.5μs的关中断时间,中断下半部出口函数OSSirqExit()屏蔽中断的时间约为4.4μs。
3.3 测试中断下半部缩短中断关闭的时间
Uart_Printf()为常用的串口打印函数,其常用于满足特定条件时通过串口打印信息。很多开发者喜欢在中断服务程序中使用它,所以,仅仅包含一句Uart_Printf()的中断服务程序,可以被认为是一个普遍而简单的应用。下面给出针对只包含一句Uart_Printf()的中断服务程序进行测试的结果。
中断上半部除设置中断相关寄存器指令外,仅包含一条Uart_Printf(“real-time test\n”),其通过串口发送一串字符。通过S3C44BOX内部定时器测试得到,CPU从中断触发到回到任务空间继续执行所花的时间为970μs,在这段时间内所有中断都被屏蔽。
若在中断服务程序中使用OSRegSirq()注册softirq,将Uart_Printf(“real—time test\n”)转移到中断的下半部执行,这时从中断发生到回到任务空间继续执行所花的时间为990 μs。总的运行时间虽然增加了20μs;但在此过程中,中断仅仅被屏蔽了30.6μs,剩下的959.4μs时间里,所有的中断屏蔽都被开启。可见,将一句简单的Uart_Printf(“real—time test\n”)移到中断下半部执行就能够节省939.4μs的中断屏蔽时间。
3.4 测试结果分析
通过以上的测试结果可以看出:中断下半部为μC/0S-II内核带来的负担极小,一次完整的中断上/下半部处理时间延长了约20μs,而关中断时间增加了约14.1μs;通过测试一个简单而普遍的中断服务应用,并采用中断下半部实现,缩短了中断关闭时间约939.4μs,这相当于94.9%的中断响应的总耗时。如果粗略地以中断屏蔽时间来衡量系统的实时性,这个测试中,使用中断下半部将实时性提高了约32倍。若对更加复杂的中断服务程序使用这种机制来进行设计,则实时性的提高将更为显着。
结 语
中断上/下半部的层次化处理机制为开发者提供了一种灵活、便捷的中断服务程序的设计方法。通过合理利用中断下半部,中断上半部的执行时间将明显缩短,中断被屏蔽的时间也会大大减少,处理器可以更快地响应中断,从而将大大减小丢失中断信号的可能性。
本设计实现了基于μC/OS-II的中断下半部。这种机制的实现充分利用了μC/0S—II的现有资源,代码简洁而高效,且与平台相关性代码极少,方便移植。一组功能定义明确的API极大地方便了开发者使用这种机制。通过在ARM7处理器上的测试表明,这种机制极大地改良了原μC/OS—II内核简陋的中断处理方式,给μC/OS—II内核带来的负担极小,却能为使用μC/OS—II的开发者带来极大的益处,对于嵌入式系统整体实时性的提高具有重要意义。嵌入式系统是“控制、监视或者辅助装置、机器和设备运行的装置”(devices used to control, monitor, or assist the operation of equipment, machinery or plants)。从中可以看出嵌入式系统是软件和硬件的综合体,还可以涵盖机械等附属装置。目前国内一个普遍被认同的定义是:以应用为中心、以计算机技术为基础、软件硬件可裁剪、适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。