初探linux子系统集之timer子系统(二)
想着博客中还没有翻译过一篇文章,虽然英文水平有限,但是借助google翻译慢慢地翻译出一篇文章也是不错的选择。那就来学习下hrtimer的文档吧,翻译的略搓,可以直接跳过这篇,这里仅作为学习的过程!^_^
hrtimers - 高精度内核timers子系统
----------------------------------------------------
这个补丁是介绍一个新的高精度内核timers子系统。
可能有人会问:我们已经有了一个timer子系统(kernel/timers.c),为什么我们需要两个timer子系统呢?在长期的来回试图将高分辨率和高精度的功能集成到现有的timer框架,并在实践中对高精度timers的实现经过各种测试后,我们最终得出了结论是timer的时间轮代码基本上说不适合这样的做法的。我们最初不相信这个(必须有一个解决方案),并发费了相当大的努力试图整合到timer时间轮里,但是我们失败了。事后看来,这种整合很难/不可能是有很多原因的:
1、强制将低分辨率和高分辨率的timers以同样的方式处理导致了很多妥协和很多#ifdef的宏。timers.c代码是非常严格地关于jiffies和采取32位数的,并且已经被磨练和优化到相对狭窄的用例很多年了,因此即使是很小的扩展,也很容易毁掉时间轮的概念,从而导致更加糟糕的妥协。timer时间轮代码是非常好的,紧凑的代码,而且在目前的使用情况下没有出现任何问题,但是它根本就不适合再扩展到高分辨率tmers了。
2、级联的不可预知的O(n)的开销会导致需要更复杂的处理高精度计时器的延迟,从而又降低了鲁棒性。这样的设计还是会引起相当大的时间不准确。级联是timer时间轮概念的基本属性,它不能被设计出在不可接受的方式中带来不必然的降低timer.c部分代码。
3、目前posix的timer子系统上timer时间轮的实现已经推出了相当复杂的处理CLOCK_REALTIME timers的settimeofday或者ntp time-我们的经验,进一步的例子:timer时间轮数据结构对于高精度timers过于严格了。
4、timer时间轮代码是可被确定的最优化的“timeout”用例。这样的超时通常设置为覆盖各种I/O路径的错误,如网络和块的I/O。绝大多数的timers永不过期,很少级联,因为预期正确的事件都及时到来,所以在需要处理它们之前就可以在timer时间轮里面删除了。因而这些超时的用户可以接受timer时间轮的粒度和精度的权衡,并在很大程度上期待timer子系统的开销接近于0。对它们来说准确不是核心目的-事实上大部分使用的超时值都是热点。对它们来说,至多是一个必要以保证超时的完成,因此,这应该是最便宜和不显眼的了。
精密timer的主要用户是用户空间的程序,它们使用nanosleep,posix的timers和itimer的接口。此外,在内核中,用户喜欢驱动程序和子系统也用timer的事件(例如多媒体),可以从一个单独的益高精度timer子系统中受益。
虽然该子系统目前还不提供高精度的时钟源,但是该高精度子系统可以很容易地扩展高精度时钟的功能,并且补丁已经存在并迅速成熟。为实时和多媒体应用的需求日益增加以及用于精确的计时器的其他潜在用户提供了另一个理由分开超时和精准计时器的子系统。
另一个潜在的好处是,这样的分离允许甚至更特殊用户在现有计时器轮低分辨率和低精度的用例的优化-一旦精确敏感API从计时器轮分离,并且被迁移过来hrtimers。例如,我们可以减少在超时子系统的频率从250HZ到100HZ(或者更小)。
hrtimer子系统的实现细节
----------------------------------------------------
基本的设计考虑是:
-简单
-数据结构不绑定jiffies或者任何其他粒度。一切内核逻辑工作在64位纳秒分辨率-不做任何妥协
-简化现有的,时间相关的内核代码
另一个基本的要求是直接入队列然后排列激活timers。看完一些可能的解决方案,比如基数树和哈希,我们选择了红黑树作为基本的数据结构。红黑树可以作为内核库,并且被用在了众多像内存管理和文件系统等的性能要求很严格的领域。这里红黑树仅用于时间排列顺序,一个单独的列表用于给出后可以让代码快速访问排列定时器,而无需遍历红黑树。
(这单独的列表对之后我们要介绍的高精度的时钟非常有用,此外我们需要分离申请中的和过期的队列,同时保持时间顺序完好)
timer排序的入队列不是纯碎用于高精度时钟而言,虽然它也简化了基于低分辨率CLOCK_REALTIME定时器的绝对的处理。现有的实现需要保持所有额外的列表绝对的CLOCK_REALTIME定时器以及复杂的锁。万一settimeofday和NTP,甚至所有的定时器不得不出队列,timer变化的代码不得不解决这些一个接着一个的问题,并且所有这些都必须再次入队列。timer排序的入队和到期时间,在绝对时间的单位存储将产出posix定时器实现的一切复杂的和缩放不好的代码-时钟可以简单地无需触摸红黑树而设置。这也通常情况下使得处理posix定时器变得更加简单。
锁定和hrtiemers的每个CPu的行为大部分是取自现有的定时器轮的代码,因为它十分成熟,非常适合。由于不同的数据结构,代码共享还不能成功。此外,hrtimer函数现在也越来月清晰明了了-比如hrtimer_try_to_cancel()和hrtimer_cancel()[这大致相当于del_timer()和del_timer_sync()]-所以没有将它们直接的1:1映射,因而没有真正潜力来实现代码的共享。
基本数据类型:每时刻time的值,绝对或者相对的,是在一个特殊的纳秒级分辨率的类型:ktime_t。内核内部是通过宏和内联函数来实现表示ktime_t的值和操作的,并且可以在混合联合类型和64位纳秒之间切换。混合联合类型优化了在32位的CPU的一次转换。这个构建时可选择ktime_t存储格式,实现避免在32位CPU上的64位乘法和除法对性能的影响。这样的操作是经常需要由内核和用户空间的接口和内部时刻格式提供的存储格式之间进行转换。(见/include/linux/ktime.h获取更多详细信息)。
hrtimers-四舍五入的计时器值
----------------------------------------------------
该hrtimer代码将以较低分辨率的时钟围绕计时器事件,因为它必须如此,否则它不会做任何人工舍入的。
一个问题是什么分辨率值应该通过返回给用户所述clock_getres()接口。这将返回一个给定时钟任何实际分辨率-无论是低分辨率,高分辨率,或者人工低分辨率。
hrtimers-测试和验证
----------------------------------------------------
我们用在hrtimers顶部的高分辨率时钟子系统验证,在实践中hrtimer实施细则,我们跑了posix计时器测试,以确保符合规范。我们也在低分辨率的时钟下试验。
该hrtimer补丁转换了下面的内核函数使用hrtimers:
- nanosleep
- itimers
- posix-timers
对于nano sleep喝posix定时器的转变启动了统一的nanosleeo和clock_nanosleep
该代码已经成功在以下平台编译成功:
i386, x86_64, ARM, PPC, PPC64, IA64
hrtimers也集成到-rt树,连同一个hrtimers型高分辨率时钟的实现,所以hrtimers代码也得到了海量的测试并在实践中使用。