Tickless 机制是Linux 内核中引入的新定时机制
摘 要:Tickless 机制是Linux 内核中引入的新定时机制,这一机制能够降低系统开销,并为高精度定时提供了实现基础。本文将对这一机制的原理进行分析,归纳出基本模型,最后提出一种全新的具有一定普遍适用性的tickless 实现方法。测试结果表明,这一新方法能够降低嵌入式系统能耗并提高操作系统定时精度。
关键词:tickless;时钟中断;嵌入式操作系统;Linux
1 引言
Tickless 机制是在Linux2.6.21 内核中引入的新定时机制,它改变了以往Linux 通过周期性时钟中断定时的状况,取而代之的是更精确的动态时钟中断(dynamic ticks)。这一机制省去了CPU 空闲时期的无意义的时钟中断,这使得系统开销大幅降低,同时定时的精确性对系统的实时性提高有很大帮助。这两个特点对于提高嵌入式系统工作性能具有重要的意义。本文将提出一种新方法来实现tickless 机制,并以s3c2410 CPU 为平台对其进行验证。
2 传统定时机制及其缺陷现有的大部分操作系统(包括 2.6.16 版本之前的Linux 内核),都使用传统的周期定时机制,该机制最突出的特点就是通过周期性的时钟中断(其中断周期称为tick)带动调度器,定时系统以及其他与时钟相关的功能模块[1]。之所以要使用时钟中来断带动操作系统,是因为操作系统必须通过硬件来把握时间,继而管理应用时间信息。
硬件平台通常会为CPU,总线等提供多个时钟信号。这些时钟信号都是由主时钟信号分频或直接得到。其中,时钟中断信号由时钟源计数器分频产生,操作系统被时钟中断触发相关时钟中断处理函数,进而带动整个操作系统的时间相关功能模块的工作。这些工作中,最重要的三项是:系统时间维护(包括时间信息读取接口),定时器任务,以及进程调度。周期定时将这三项工作驱动,就能够使操作系统的时间相关部分基本运转起来。
其中,系统时间的维护需要频繁的时间提示信息,才能够较精确地记录当前的时间。为了满足这一点要求,时钟源提供周期性的时钟中断,来提示系统一个时间单位已过,是一种顺理成章的实现方式,这种方式也就是大部分操作系统采用的定时(计时)方式。在此基础上,系统时间自然是以时钟中断周期——tick 作为单位。另外两项工作:定时器任务,以及进程调度也都以这样的定时方式为基础实现其功能。
进程调度方面,许多调度策略虽各自不同,但通常都会被时钟中断触发(其它一些事件也会触发调度)。在周期定时机制下,每一次时钟中断都会带动一次是否需要调度的检查,操作系统多以这种方式来确定当前执行调度的条件是否满足[2]。但是,并非每一次时钟中断都会发生进程的调度,能够触发进程调度的时钟中断往往只占其中很小一部分。
定时器任务(这里指操作系统提供的动态定时器)必须被时钟中断触发才能实现,定时器系统会按照到期时间的先后顺序“排列”已被注册的定时器,并被周期性的时钟中断带动,每一tick 检查一次是否有定时器到期,若到期,则调用该定时器的回调函数[2]。通常,大部分tick 内是没有定时器到期的,仅有少数的时钟中断会触发定时器事件。 虽然传统的周期定时机制完善的实现了对上述操作系统工作的服务,但服务中的缺陷也是显而易见的。
首先,周期性的时钟中断造成不必要的系统能耗:
在操作系统运行的很多时段内,除了系统时间维护,时钟中断对系统的运转没有实际意义——不会触发进程调度以及定时器事件,只能起到更新系统时间的作用。这样,“无意义”
的时钟中断又带动了系统的“无意义”工作,从而造成了不必要的能耗。大量的实验观察结果表明,若不考虑系统时间更新,两个对操作系统真正有意义的时钟中断间,存在大量无意义的时钟中断,特别是在系统空闲时段内。嵌入式系统的空闲时间比例通常在95%以上,这些时间内系统没有任何进程需要调度执行,也没有定时器到期,但时钟中断仍在周期性地产生,而每一次中断(未被屏蔽情况下)都会引出一串不必要的固定工作,包括寄存器内容切换等。考虑到很多CPU 都具有休眠功能,若这些在系统空闲时段内产生的不必要时钟中断被消除,CPU 就能在大部分运行时间内真正休眠节能(不同于周期定时下总是被唤醒的不理想节能状况)。
第二,周期性定时机制使系统定时精度难以提高。
周期定时机制下,各种被时间触发的操作都与tick 挂钩,即每一次时钟中断带动一次操作,其中就包括检查当前tick 是否有动态定时器到期。这种实现方式也就决定了操作系统的定时以及其它时间信息处理的精度最高只能以ticks 为单位。通常的嵌入式硬件平台上,一个tick 被设置为5ms~10ms,也就是说系统的定时精度只能达到5ms~10ms。
对于一些应用场合来讲,这一定时精度相当不理想。为了提高精度,最直接的办法是将tick 的周期设置得足够小。这个方法看似可行,但实际上不值得采纳,应为这样做会大幅降低CPU 处理实际任务的有效利用率(不包括中断处理),使CPU 花更多比例的时间来处理时钟中断[3]。测试结果(s3c2410 平台Linux2.6.12 内核,可抢占模式)如图2所示,可以看出,CPU 有效利用率随定时精度提高下降得非常快。
3 Tickless 机制的原理
由于上面提到的问题在周期定时机制下,难以得到根本的解决,操作系统需要一种全新的定时机制,tickless 机制就是一种操作系统处理时钟信号的新方式。Tickless 机制出现于Linux2.6.21 内核,其主要原理是以下三点:
(1) 通过预期省略不必要的时钟中断。由于大部分周期性的时钟中断不会触发进程调度和定时器事件,系统就可根据对下一次需要执行调度的时刻或者未来最早的定时器到期时刻的预期,确定下一次“有意义”时钟中断的产生时刻,而不再让其余“无意义”的时钟中断产生。这样一来,不论在系统实际工作期间还是在空闲期间,都有大量的不必要的中断处理工作被省去,从而节省了能量。
(2) 将定时器任务与周期定时脱钩,提高定时精度(主要是定时器精度)。这一原理通过图3 的比较很容易理解。在省略了不必要时钟中断的基础上,两个时钟中断间的时间间隔变得稀疏,这一间隔粗略代表了相邻时间事件的发生时差,而不再像周期定时下的中断间隔代表定时周期并与定时精度一致。因此,我们就能将定时时间单位的粒度大幅缩小,让时钟中断更加逼近其代表的时钟事件的发生时间。在此基础上,就能够使定时器的实际到期时间更加接近预期时间,定时精度提高。
(3) 原有周期性时钟中断所携带的时间信息需要恢复。上文提到过,传统周期定时机制下,每一个时钟中断被赋予了时间信息,表示一个单位的时间已过,正因为此信息,系统时间才能正常更新,若时钟中断不再周期产生,这一信息将丢失。因此,为了使系统时间更新等工作在非周期定时下正常运转,时间信息的恢复必不可少。虽然周期性的时钟中断已被抑制,但仍可以 tick 为单位度量空闲时间段,在实际中断产生后,补充被抑制的时钟中断应该产生的触发效果,从而恢复时间信息。
4 Tickless 机制
在Linux 内核中的实现方式Tickless 机制在Linux 内核中的具体实现结构如图5 所示,主要由三个部分组成:GenericTime Of Day(GTOD)子系统,Clock Event 子系统,以及Hrtimer(高精度定时器)子系统[4]。GTOD 对操作系统提供时间信息接口,内核能通过GTOD 获取当前精确时刻(内核只能主动获取),Clock Event 子系统对各种时钟事件提供了统一的抽象和管理层,时钟中断的分发以及预定就是这部分的主要工作。Hrtimer 用于实现精确定时,同时针对一部分兼容性问题,通过特定函数转换为传统定时系统接口,以新方式提供传统的服务。是对其结构的简要描述,图中新定时子系统就是功能上实现省略无意义时钟中断的主要部分,这一部分与硬件时钟源直接互动,设置并处理时钟源的寄存器及其产生的中断,使时钟中断有预期地被设置产生,而不是周期性地产生。具体实现细节较繁杂,这里不再冗述,但有一点需要注意,就是时间信息的丢失及恢复的问题。从图中可以看出,整个tickless 定时系统为操作系统提供了两类接口:传统接口和新接口。这两类接口虽然都是时钟信息接口,但却有较大的差别。
新接口主要对内核中由 tickless 造就的新功能提供服务,例如高精度定时器,空闲时段设置,进程时间片耗尽提示(不同于以往的每个tick 由内核主动检查当前进程是否耗尽时间片)等。这类接口需要调用者提供定时信息,而它自身仅提供时钟中断信息,即告诉相关功能模块,当前有对应时钟中断产生,至于中断产生的时刻完全由调用该接口的任务自己负责。
可以看出,新接口提供的服务只对主动调用它们的任务有意义。然而,以往操作系统中的一些功能模块都需要被动接收时间信息才能工作。为了从tickless 机制下恢复时钟中断原本携带的时间信息,Linux 内核在新定时子系统与传统定时系统间建立起转换关系:由传统定时系统调用新接口,同时又对中断信息中的时间信息进行恢复,最终提供带有时间信息的时钟事件信息,模拟出传统接口。
5 Tickless 机制实现的新方法
Linux 内核并没有为所有硬件平台提供tickless 的实现,截至最新版本的内核,很多嵌入式开发常用的CPU 仍然没有对应的tickless 支持代码,而其它操作系统中,尚无tickless机制的实现。
从上文中可以看到,Linux 内核实现tickless 的具体结构较复杂,对整个定时系统作了较彻底的改变,另外也有其针对自身特点的考虑。在嵌入式开发中,如果依照该结构来改写操作系统代码,不仅工作量相当大,而且改动后稳定性和实用性也很难保障。
相比之下,这里提出一种更简单的 tickless 实现方法。这一方法针对的是原本采用周期定时的操作系统中时间相关模块的普遍结构,而非具体某一操作系统的特殊架构,因此具有相对广泛的适用性。(这一方法已在S3C2410 平台上成功实现,基于没有tickless 支持的Linux传统内核。)首先需要明确的是,这一类采用周期定时的操作系统中,只要系统时间维护,定时器任务,以及进程调度能够正常运转,操作系统的时间相关部分就能基本运转起来(一些操作系统特有功能也许不能正常工作)。通过上文的分析,可以得出以下结论:系统时间维护的正常运转,需要时钟中断带来的时间信息才能正常工作,如果不定长中断间隔的时间信息能被恢复,密集或周期性的时钟中断并不是提供准确时间信息的必要条件;定时器任务方面,触发一个定时器只需要一个时钟中断,但这个时钟中断必须与定时器到期时间直接关联,其产生时间尽可能靠近定时器到期时间,这样就能正常工作;进程调度需要的时钟中断服务与定时器任务类似,仅需要能准确触发实际调度的时钟中断,即可正常运转。
根据上面的结论得知操作系统时间相关部分正常运转的必要条件,接着就能通过改动内核相关函数代码进行tickless 机制实现。不过,这里考虑到实现的复杂度,以及改动后的实际效果,决定保留系统在进程/任务实际执行时段内的周期性时钟中断。这样,新定时机制的基本工作就设计为下面四点:
(1) 在系统工作时,沿用周期性的时钟中断,以减少对调度器及相关部分的修改;(2) 当系统进入空闲状态时,停止周期性时钟中断,将下一次时钟中断的时刻设置为预期中断产生时刻,这一时刻为时钟源最大定时范围和下一个定时器到期时刻这两者中的最近者;(3) 在空闲状态下,若有外部中断产生,则立刻恢复周期性时钟中断;(4) 对不定长的时钟中断间隔,以原有tick 为单位度量其长短,并补充相应的更新操作。这一机制下,定时系统结构的改动。
系统时间维护部分,在接收到被恢复的时间信息,得知上一次中断间隔为 N ticks 后,循环调用更新函数N 次,以补足应有的更新操作。在系统非空闲的状态下,N=1,即系统时间仍能频繁被维护,时刻保持较准确的计数,而在空闲状态下,虽然系统时间长时间得不到更新,但在空闲时段内,没有任何操作需要读取系统时间,因此,系统时间维护部分不但自身能正常运转,也能为其它功能模块提供准确信息。
定时器任务部分,当定时系统预设下一时钟中断的产生时间时,定时器组织结构会被查询以找出最近的一个定时器到期时间,若这一时间相比预设需考虑的其它时间短,则将其设为下一时钟中断的产生时间。这样,定时器任务部分能够正常运转。
进程调度部分,在系统非空闲的状态下,它的工作方式与原操作系统一致,仅在系统空闲时,它才会调用新增的时钟中断设置函数,试图停止周期性的时钟中断,所以转让一部分也能够正常运转。
综上可知,这样的基本改动使原有大部分不必要时钟中断被省略(因为系统大部分运行时间处于空闲状态),使定时器触发与tick 脱钩,同时又能使系统正常运转。接下来,需要进一步的改动以发挥tickless 机制的优势。
第一,发挥节能效果。即在空闲状态时,通过idle 进程/函数,切换CPU 状态为节能状态。由于经上述改进,空闲状态下两个中断时间间隔通常很长,因此CPU 能够长时间处于节能状态而不被唤醒,从而节省不少的能量。(在有些操作系统提供的驱动代码,能够使CPU在idle 进程/函数中睡眠,但在周期定时下,其效果不理想。)第二,由于定时器触发已与tick 脱钩,就能在此基础上大幅缩小定时时间单位(例如将单位tick 换算为微秒),使定时器的到期时间更加贴近与之关联的实际时间事件,从而使高精度定时器得以实现。
至此,无论从可行性还是有效性来看,tickless 机制都得到了较完全的实现。
6 测试结果
这里以 S3C2410 为平台,分别对未改进的linux 内核以及通过新方法实现了tickless 机制的内核进行了测试。
首先是能耗方面的测试。系统在运行的绝大部分时间(通常 90%以上)处于空闲状态,此时内核会让CPU 进入idle 状态以节能,进入这一状态的CPU 会被时钟中断和其余外部中断唤醒,直到系统对中断做出必要处理后,若无任务被唤醒或创建,系统才会让CPU 再次进入idle 状态。因此,在系统空闲时,CPU 的功率会因为时钟中断出现周期性波动。图8显示了这一状况,图中Ti 时段即是CPU 处于idle 状态的时段,Tc 则是CPU 被时钟中断唤醒后执行中断处理例程的时间长短(系统空闲时),Tc 时段中,CPU 处于正常工作状态。测试中对Tc 和Ti 起始时间作了记录,由测试结果计算出:在周期定时机制下Tc 的平均值为476us,Ti 的平均值为4524us,而tickless 机制下,Tc 平均值为512us,Ti 平均值是0.67s(相比之下,Tc 的影响几乎可以忽略)。根据s3c2410 芯片手册,在200MHz 工作频率下,正常状态CPU 典型功率为259mW,idle 状态CPU 典型功率为124mW,由此可以计算出在系统空闲状态下,采用tickless 机制的系统相比采用周期定时的系统节能8.6%。
定时精度方面,实现了tickless 机制的内核定时器精度在0.5ms 到1ms 左右(主要受ARM处理能力影响),相比周期定时内核的5ms,精度已有较大幅度提升。并且,如果将指令执行时间算作统计提前量,大部分的定时器任务(一定的概率下)还能更加精确。
7 结论
本文在对 tickless 机制模型化的基础上,提出了更具广泛适用性的tickless 实现方法,经过试验验证,经该方法实现的tickless 机制能够有效降低嵌入式系统运行能耗,同时能够提高操作系统定时精度。因此,这一实现方法对嵌入式操作系统的性能改进有很大意义。
十年论文机构京都名师论文中心,正规全面的论文刊物为您提供职称论文,毕业论文,硕士论文,医学论文,教育论文等各类论文发表服务。
参考文献
[1] Christian Benvenuti.Understanding Linux Network Internals[M].O'Reilly Media, Inc, 2005
[2] 毛德操,胡希明.Linux 内核源代码情景分析[M].杭州:浙江大学出版社,2001
[3] 周鹏,周明天.Linux 内核中一种高精度定时器的设计与实现[J].计算机技术与发展,2006,16(4):
73-75.
[4] Thomas Gleixner, Douglas Niehaus.Hrtimers and beyond - transformation of the Linux time(r) system