您查看的文章来源于http://www.oklinux.cn
下面让我们来详细考察一下:
- 将 TB 寄存器的值读入 64 位变量 tbr 中。这需要使用汇编语言实现,因为在 C 语言中没有提供相应机制来访问这个寄存器。
__asm__ __volatile__ ("mftb %[tbr]" : [tbr] "=r" (tbr):);
- 计算 TB 寄存器和
tb_orig_stamp
之间的差值作为 elapsed_ticks_since_boot
。
elapsed_ticks_since_boot = tbr - cfg->tb_orig_stamp;
- 计算
elapsed_ticks_since_boot
和 tb_to_xs
的乘积作为 elapsed_xseconds_since_boot
multiply(elapsed_ticks_since_boot, cfg->tb_to_xs,
&elapsed_xseconds_since_boot_hi,
&elapsed_xseconds_since_boot_lo);
注意我们很有必要使用一个辅助乘法函数。这两个操作数 elapsed_ticks_since_boot
和 cfg->tb_to_xs
都是 64 位长的,它们的乘积是一个 128 位的数字。这么大的值无法使用一个 C 变量来表示,因此这个辅助乘法函数会将其分成两部分,在本例中分别称为 elapsed_xseconds_since_boot_hi
和 elapsed_xseconds_since_boot_lo
。乘法的详细信息会在 乘法辅助函数 一节中介绍。
- 计算
elapsed_xseconds_since_boot_hi
和 stamp_xsec
之和作为 xseconds_since_epoch
。
xseconds_since_epoch =
cfg->stamp_xsec + elapsed_xseconds_since_boot_hi;
- 将
xseconds_since_epoch
除以 xseconds_per_second
(右移 20 位)转换成秒,并将结果乘以 nanoseconds_per_second
转换成纳秒作为 作为 nanoseconds_since_epoch
。注意这个算法首先执行一个乘法,然后又执行一个除法(移位)。这样可以提供更好的精度。
multiply(xseconds_since_epoch, nanoseconds_per_second,
&nps_temp_hi,
&nps_temp_lo);
nanoseconds_since_epoch_lo = nps_temp_lo >> 20;
nanoseconds_since_epoch_hi = nps_temp_hi << (64 ? 20);
nanoseconds_since_epoch =
nanoseconds_since_epoch_hi | nanoseconds_since_epoch_lo;
|
请注意:大部分计算都避免更改 tb_update_count
值,该值保存在 count_start
和 count_end
变量中。这是用来确保从 tb_to_xs
和 stamp_xsec
变量中读出的值是一致的。这种变量的值可以在操作系统检测到更新频率发生变化或操作系统进行频率更改时进行改变。
Linux libc 库解释了 tb_update_count
是如何更新的,以及如何对这个值进行解释:
更新这些变量的代码会增大 tb_update_count
的值,然后更新这两个变量,并再次增大 tb_update_count
的值。这段代码会读取 tb_update_count
的值,读取这两个变量值,然后再次读取 tb_update_count
的值。它会循环处理这个过程,直到两次读取 tb_update_count
可以产生相同的结果并且这个结果是偶数为止。这可以确保能够得到这两个变量的一个一致视图(请参阅 参考资料 中的引用)。
另外,tb_update_count
还会用来检测 TB 寄存器何时会溢出。当发生这种情况时,会按照相同的协议执行;tb_update_count
会在必须的修改反映到其他变量中时进行更新。操作系统需要负责检测这种情况。
最后,值得一提的一件有趣的事情是一个 64 位的纳秒值可以表示大约 585 年的时间。