一、内核定时器1.基本概念
在个别场景下,我们须要在特定的时间后做个别动作,而且又不想仍然等待而浪费CPU,这个时侯定时器是十分合适的机制。定时器用于在将来的某个时间点执行某个函数以完成特定的任务。
内核定时器告诉内核在指定的时间点使用特定的参数来调用特定的函数。定时器是异步运行于其注册者的,定时器运行时,注册该定时器的任务可能在休眠也可能在其它处理器上运行,甚至可能早已退出。
linux中内核定时器是基于(软)中断实现的linux 应用定时器,也就是它处于中断上下文而非进程上下文。在非进程上下文有些原则要遵守:
不准许访问用户空间
current无意义,因此也不可用
不能进行睡眠或则调度。不能调用schedule或则某种wait_event,也不能调用任何可能造成睡眠的函数。讯号量也不可用,由于讯号量可能导致休眠。
内核代码通过调用函数in_interrupt()可以判定当前是否处于中断上下文,只要返回非0就表示处于中断上下文。内核可以通过调用in_atomic()判定当前是否容许调度。不容许调度的情况包括:处于中断上下文以及拥有载流子锁的上下文。
因为定时器是异步执行的,因此定时器处理函数必须注意进行互斥保护。
2.linux内核支持的定时器
linux内核支持两种类型的定时器:
精典定时器:精度取决于计算机时钟中断运行频度的定时器。该定时器精度通常比较低,为1000/HZms的精度。该定时器以固定的频度形成,即每隔1000/HZms形成一次。若果没有使能动态时钟特点,则在该定时器到期时,可能并没有真正的定时风波发生,例如系统中只添加了如下定时器:11ms,52ms,78ms到期的定时器,而且精典定时器会在4的倍数ms处到期(4,8,12...),因此说定时器到期的时间点不一定有定时风波发生。
高帧率定时器:精典定时器的精度比较低,在有的场合须要更高精度的定时器,例如多媒体应用中文linux操作系统,因此系统引入了该类型的定时器。该定时器本质上可以在任意时刻发生。
这儿也须要非常之处两个概念:
动态时钟:只有在有任务须要实际执行时,才激活周期时钟linux 应用定时器,否则就禁用周期时钟的技术。作法是假如须要调度idle来运行,禁用周期时钟;直至下一个定时器到期为止或则有中断发生时为止再启用周期时钟。单触发时钟是实现动态时钟的前提条件,由于动态时钟的关键特点是可以按照须要来停止或重启时钟,而纯粹的周期时钟不适用于这些场景。
周期时钟:周期性的形成时钟时间的时钟。
从应用上来说,定时器主要有两个用途:
超时:表示在一定时间后会发生的风波,事实上,在使用超时时,大多数情形下,并不期望超时时间发生,定时器常常会在超时之前被取消。另外虽然不取消,超时风波也常常不是一个精确的风波,例如在网路中用到的各类超时定时器,它们抒发的意思常常是假如在这个时间点之前还没有...,则可以觉得...,这个时间的取值常常是一个经验值或则计算值,并不是精确的时间要求。在这些场合精典定时器是够用的。
定时器:用于实现时序,例如播放声音时,须要定期往声卡发送数据,这些场合下,对时间就有比较严格的要求,倘若不在某个时间点发送数据到声卡,还会出现声音失真。这时就要使用高精度定时器。
通过配置可以促使linux工作在如下模式:
高帧率动态时钟
高帧率周期时钟
低码率动态时钟
低码率周期时钟
3.低码率内核定时器
低码率定时器是最常见的内核定时器,内核会使用处理器的时钟中断或则其他任何适当的周期性时钟源作为定时器的时间基线。时钟中断会定期发生,每秒HZ次。该中断对应的定时器处理函数通常为timer_interrupt,在该函数的处理中,最终会调到do_timer和update_process_timers。其中do_timer会负责全系统范围的、全局的任务:更新jiffies,处理进程统计;而后一个函数则会进行进程统计,形成TIMER_SOFTIRQ,向调度器提供时间感知。
当定时器到期时,在定时器处理函数被调用之前,定时器会被从激活数组移走,因此假如想要在本次执行完后再过一段时间后重新执行,就须要重新添加定时器。在SMP系统中linux中文乱码,定时器函数会由注册它的CPU执行。
内核定时器的实现要满足以下要求和假定:
定时器管理必须尽可能简化.
其设计必须在活动定时器大量降低时具有挺好的伸缩性
大部份定时器在几秒或最多几分钟内到期,而带有长延时的定时器是相当稀少.
一个定时器应该在注册它的同一个CPU上运行.
低码率内核定时器的实现是很巧妙的。它基于一个每-CPU数据结构。timer_list的base数组包含了指向该结构的表针。假如base是NULL,这个定时器没有被调用运行;否则,这个表针告知那个数据结构(也就是那个CPU)正在运行它。
无论何时内核代码注册一个定时器(通过add_timer或则mod_timer),操作最终由internal_add_timer执行(在kernel/timer.c),它会将新的定时器添加到和当前CPU关联的“级联表”中的定时器单向数组中。
级联表的工作方法:
假如定时器在接出来的0到255jiffies内到期,则它被添加到特供短时定时器的256个数组中的一个,使用expires(即添加到哪个数组是由到期时间的比特位决定的)的低8位决定加到哪个数组中
假如它在将来更久时间到期(但又在16384个jiffies之前),则它被添加到64个数组之一,这64个数组与expires的8-13比特位相关,expires的这6个比特决定了使用那个数组。
类似的技术被应用于expires的比特位14-19,20-25以及26-31。
假如定时器在更久的经来到期
以上就是深入了解 Linux 内核定时器:基于中断的异步机制与非进程上下文原则的详细内容,更多请关注CTO智库其它相关文章!