基于前向反馈的TSC时钟
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
正文 :
NTP 网络校时已有20年的历史,其核心同步算法(校准主机时钟漂移)是反馈(PLL/FLL )。聚类算法从候选参考源中选择最佳者,减少网络时间漂移产生的不良影响; 时钟调节算法校准计算机时钟的时间,补偿固有频率误差,根据测得的抖动和漂移动态地调节相关参数。
符合IETF RFC1589 标准的开源操作系统提供反馈同步算法,如下图所示,系统调用gettimeofday()、adjtime() 形成图一这样的后向反馈。
图二 后向反馈的一般模型
现在的个人计算机都内嵌了TSC 、ACPI (Adva nced Confiuration and Power Interface )、HPET (High Precisoin Event Timer ),在计算机操作系统启动时,这些计数器都为0,然后以各自相应的振荡器频率计数。计算机操作系统选择一个认为可靠的计数器,并提供一个函数调用接口去访问它,linux Kernel 为clocksource ,FreeBSD Kernel 为timecounter 。
这些计数器的溢出产生典型值为1ms “硬件”中断。当程序执行一个gettimeofday()系统调度,或者当“硬件时钟”产生中断时,常规NTP 算法用Kernel 生成一个系统时钟时间戳(time record)。
Kernel 读取当前计数器值(counter record)并计算出δ,即上一次的计数器记录与当前值之差值。Kernel 调用adjtime()函数将δ转化成秒数,与上一次的时间记录(time record )相加,就可得到系统时钟时间戳。
如果是执行一个gettimeofday()系统调度,那么生成系统时钟时间戳将送回给用户程序;如果由“硬件时钟”中断触发,那么生成系统时钟时间戳将变为新的时间记录,同时当前计数器值也成为新的计数器记录。
在网络通信环境下,常规NTP 算法打时间戳具有不确定性,呈高斯分布,并挟带少量尖峰信号,这严重影响NTP 反馈算法的实现精度,并可能导致NTP 反馈算法失效。目前NTP 的精度是毫秒级。
adjtime() gettimeofday()
为适应这种网络通信环境的应用,提出了TSC-CLOCK这个概念。TSCclock 属于RADclock,它运用了差分时钟,不需要校准漂移,测量时间差可以非常准确。Difference(差分)TSCclock,为测量p提供精确且高健壮性的算法。在同侧同一位置打时戳,通过前后不同包的时戳来计算时间差,避免了频差,也就是漂移的影响。
对比基于ntpd的软件架构,TSCclock不会以反馈环的方式频繁改变速率来校准漂移,而是以前馈方法获取长期稳定的时钟速率估计值。从而提供了差分时间和绝对时间的两个时钟。
定义未修正的时钟Cu(t)= TSC(t)ˆp +K,这里TSC(t)是计数器计数值,ˆp 是时钟周期估计值,K是初始相位差的估计值,它是不更新的常数。在每个新的时戳进入时,更新(校正)Cu(t)的误差估计值ˆθ,那么
,所以差分时钟Cd(t)与误差校正值ˆθ和漂移常数K都无关。
RADclock基于硬件的计数器,系统开机时清零。它连续计数可以永远不翻转,并且访问时间要求短。
现在计算机硬件具备64位的TSC,如果计数频率为1GHz,64位的计数器大约需要585年才会溢出。通过rdtsc()调用可以读取。在目前的PC硬件架构中,该函数读取的值易受其他功能的影响,如电源管理、频率步进(frequency stepping)多核处理等,存在不稳定与时间不连续的情况。如果屏蔽一些功能,情况可能要好,但这在实际应用中是不现实的。幸运的是,TSCclock仅仅是RADclock算法的其中一种实现方式。
我们修改了linux Kernel,在clocksource增加了对64位累积计数器记录(cumulative counter record)的系统调用函数getcounter()。当程序执行getcounter()系统调用或有“硬件”中断时,Kernel将读当前累积计数器,并计算出上一次的计数器记录与当前值之差值δ。然后用δ和上一次累积计数器记录求和得到当前累积计数器值。注:当前累积计数器值是不断变化的,而累积计数器记录在刷新前是恒定的。如果getcounter()系统调用产生,当前累积计数器值将返给用户。如果是硬件中断产生,当前累积计数器值成为新的累积计数器记录。
图三前向反馈的模型(j
相对常规的NTP客户端算法,这种方式要简单得多,它去除了adjtime()将δ转换成秒数,并与上一次时间记录作加法,生成系统时间戳这样一个繁琐过程。另外,它不需要软件去维持时间计数,只需要Kernel增加一个原始计数器时间戳读取接口。这个接口通过一个适用于内核内部和用户侧程序的新型数据结构来实现。
它是绝对鲁棒和差分时钟(RADclock)的详细实现。
RADclock提供一个绝对时间和一个差分时钟,基于实用的原始硬件计数器(即目前流行的PC自有硬件),该计数器连续单增且空间足够大而不会翻转。
差分时钟:一个不修正漂移的时钟,用于准确测量时间差。每次同步基于一个独特的(前向)反馈算法和一个具有高健壮性的噪声滤波器。
TSC时钟采用计算CPU时钟周期的TSC寄存器,它具有64位宽并且仅仅只需要增加少量几行代码组成rdtsc()函数来实现内核或者用户侧的访问(存取)。
TSC劣势:在现代的(PC)硬件系统中,诸如电源管理、渐进式变频和处理器多核之间的不同步会影响rdtsc()函数返回值的稳定性和一致性。可以通过禁用这些特殊功能来解决此问题,但对产品来说这不是一个现实解决方案。
但幸运的是TSC时钟不是RADclock的唯一实现方案。文中实验证明HPET(高精度事件定时器)ACPI(先进配置和电源接口定时器)跟TSC寄存器的解决方案性能很接近。
上图描述了RADclock通过时间计数器接口使用原始的rdtsc()函数和各个计数器的差错。每种分布对应了RADclock采用NTP协议同步到LAN的stratum-1服务器一整周的捕获数据。跟预期一致,RADclock性能和所选计数器无关。
同步算法性能受到极端温度变化和网络活跃性的影响,而各个计数器的性能相近。
即本文研究的任何一个计数器都很适于守时(timekeeping)。
NTP内核算法:
操作系统会在系统启动时选择它认为是最可靠的一个计数器并分配一个接口来访问它。
该接口位于内核内部,在FreeBSD中叫时间计数器;在Linux中叫时钟源
可用的计数器每分钟会溢出几次,内核机制跟踪(记录)计数器溢出事件,进而维持一个一致的时间概念(通过进位来实现),按以下步骤来工作:
1、在每个系统中,“硬件时钟”产生由内核捕获的中断(典型值为1ms一次);
2、每次中断,内核产生两个时间戳。其一为当前硬件计数器的读数(计数器记录);而另一个由系统时钟产生(时间记录)。
3、当程序发布一个gettimeofday()系统调度,或者当“硬件时钟”产生中断时,内核就产生一个系统时钟时间戳。内核在此端读取当前计数器值并计算出δ,即上一次的计数器记录与当前值之差值。内核将时钟周期数δ转化成秒数,并加上上一次的时间记录再返回结果。如果创建时戳是由gettimeofday()触发的,那么将时戳送回给用户侧;如果由“硬件时钟”中断触发,那么此时戳变为新的时间记录,同时计数器读取新的计数器记录。
前向反馈的改进:
A、理想计数器:尺寸足够大而不会溢出,高稳定度,自动快速的存取。
前向反馈增加了这么一个计数器,包含一个64位的累积计数器记录,附加到时间计数器和时钟源接口。64位字段的空间足够大而可以保证不会溢出(roll over)。