第一百五十章:时钟
合道期涉及内核源码:
一
林小源走进设备山脉的一座钟楼。钟楼内部弥漫着一种均匀而持续的滴答声——每一声滴答都精确得像是用尺子量过的。
钟楼的中央悬挂着一个巨大的钟摆,钟摆每摆动一次,墙上的一组齿轮就转动一格。齿轮上刻着一个数字:jiffies。数字随着每一次滴答跳动——1024、1025、1026——永不停歇。
一个须发皆白的老者站在钟摆旁,手中拿着一根权杖,权杖顶端嵌着一颗闪烁着蓝光的晶石。
"我是系统时钟。"老者的声音和钟摆的节奏完全同步,"jiffies 是我的心跳——自系统启动以来,每秒钟跳 1000 次。HZ=1000,一毫秒一跳。"
林小源抬头看着那组齿轮。jiffies 的数字已经跳到了一个天文数字——系统从启动到现在,已经过了多少毫秒,这个数字就有多大。
"所有的时间都基于 jiffies 吗?"林小源问。
"大部分是。"老者用权杖敲了敲墙上的一个面板,面板上亮起了 "timer_list" 的字样,"这是软件定时器——你设定一个超时时间,比如 jiffies + HZ,那就是一秒钟后触发。定时器到期时,在软中断中执行回调函数。"
林小源注意到钟摆的旁边还有一座更小的钟——那座钟的刻度不是毫秒,而是纳秒。
"那是 hrtimer——高精度定时器。"老者指着小钟说,"传统定时器的精度是一毫秒,对大多数场景够用了。但如果你要同步音频、视频,或者做性能测量,一毫秒的精度远远不够。hrtimer 可以精确到纳秒。"
林小源望着那座永不停歇的钟摆,心想:整个内核的时间,都系在这根摆锤上了。
/*
* 内核的时钟:
*
* 1. 系统时钟 (system clock)
* jiffies — 自启动以来的时钟滴答数
* HZ — 每秒的滴答数
*
* 2. 高精度定时器 (hrtimer)
* 纳秒精度
* 用于定时器、sleep
*
* 3. TSC (Time Stamp Counter)
* CPU 的时钟计数器
* 最高精度
*
* 定时器:
* struct timer_list:
* 软件定时器
* 在软中断中执行
*
* hrtimer:
* 高精度定时器
* 纳秒精度
* 用于 nanosleep, usleep
*/
printf("=== 时钟 — 内核的时间基础 ===\n\n");
printf("内核的时钟:\n\n");
printf("1. 系统时钟:\n");
printf(" jiffies — 时钟滴答数\n");
printf(" HZ — 每秒滴答数\n");
printf(" HZ=1000: 每毫秒一次\n\n");
printf("2. 高精度定时器:\n");
printf(" 纳秒精度\n");
printf(" hrtimer\n\n");
printf("3. TSC:\n");
printf(" CPU 时钟计数器\n");
printf(" 最高精度\n\n");
printf("--- 定时器 ---\n");
printf("struct timer_list:\n");
printf(" 软件定时器\n");
printf(" 在软中断中执行\n\n");
printf("使用:\n");
printf(" timer_setup(&timer, callback, 0);\n");
printf(" timer.expires = jiffies + HZ;\n");
printf(" add_timer(&timer);\n\n");
printf("--- hrtimer ---\n");
printf("高精度定时器:\n");
printf(" 纳秒精度\n");
printf(" 用于 nanosleep, usleep\n\n");
printf("使用:\n");
printf(" hrtimer_init(&timer, CLOCK_MONOTONIC,\n");
printf(" HRTIMER_MODE_REL);\n");
printf(" hrtimer_start(&timer, ns, ...);\n\n");
printf("--- 时间相关系统调用 ---\n");
printf("clock_gettime(): 获取时间\n");
printf("nanosleep(): 高精度睡眠\n");
printf("alarm(): 设置闹钟\n");
printf("setitimer(): 设置间隔定时器\n");#include <stdio.h>
/*
* 内核的时钟:
*
* 1. 系统时钟 (system clock)
* jiffies — 自启动以来的时钟滴答数
* HZ — 每秒的滴答数
*
* 2. 高精度定时器 (hrtimer)
* 纳秒精度
* 用于定时器、sleep
*
* 3. TSC (Time Stamp Counter)
* CPU 的时钟计数器
* 最高精度
*
* 定时器:
* struct timer_list:
* 软件定时器
* 在软中断中执行
*
* hrtimer:
* 高精度定时器
* 纳秒精度
* 用于 nanosleep, usleep
*/
int main() {
printf("=== 时钟 — 内核的时间基础 ===\n\n");
printf("内核的时钟:\n\n");
printf("1. 系统时钟:\n");
printf(" jiffies — 时钟滴答数\n");
printf(" HZ — 每秒滴答数\n");
printf(" HZ=1000: 每毫秒一次\n\n");
printf("2. 高精度定时器:\n");
printf(" 纳秒精度\n");
printf(" hrtimer\n\n");
printf("3. TSC:\n");
printf(" CPU 时钟计数器\n");
printf(" 最高精度\n\n");
printf("--- 定时器 ---\n");
printf("struct timer_list:\n");
printf(" 软件定时器\n");
printf(" 在软中断中执行\n\n");
printf("使用:\n");
printf(" timer_setup(&timer, callback, 0);\n");
printf(" timer.expires = jiffies + HZ;\n");
printf(" add_timer(&timer);\n\n");
printf("--- hrtimer ---\n");
printf("高精度定时器:\n");
printf(" 纳秒精度\n");
printf(" 用于 nanosleep, usleep\n\n");
printf("使用:\n");
printf(" hrtimer_init(&timer, CLOCK_MONOTONIC,\n");
printf(" HRTIMER_MODE_REL);\n");
printf(" hrtimer_start(&timer, ns, ...);\n\n");
printf("--- 时间相关系统调用 ---\n");
printf("clock_gettime(): 获取时间\n");
printf("nanosleep(): 高精度睡眠\n");
printf("alarm(): 设置闹钟\n");
printf("setitimer(): 设置间隔定时器\n");
return 0;
}二
林小源在钟楼的二层发现了一排排整齐的计时沙漏。每个沙漏上都贴着标签——有的写着 "网络超时",有的写着 "磁盘 I/O",有的写着 "进程调度"。
"这些沙漏都是基于 jiffies 工作的,"系统时钟老者说,"网络协议栈需要超时重传,磁盘 I/O 需要超时处理,调度器需要时间片计时——它们都依赖我的 jiffies。"
林小源拿起一个 "网络超时" 的沙漏。沙漏里的沙子正在均匀地流逝,每一粒沙子代表一个 jiffy。
"如果沙子流完了,请求还没收到响应呢?"林小源问。
"那就超时了——触发重传或者报错。"老者敲了敲沙漏底部,"jiffies 是内核时间的基本单位。它不是墙钟时间——它只关心系统运行了多久。系统休眠的时候,jiffies 不走。"
林小源注意到有些沙漏的沙子流得很快,有些很慢。那些标着 "高精度" 的沙漏用的是透明的纳米级颗粒——hrtimer 的精度。
"nanosleep() 和 usleep() 都走 hrtimer,"老者说,"如果你用传统的 timer_list 睡一毫秒,实际可能睡了两毫秒——因为 jiffies 的精度就是一毫秒。hrtimer 不会给你这种误差。"
他忽然明白,jiffies 就是内核的心跳——一下一下,驱动着所有的时间机制。
三
钟楼的最高层是一个密室,密室中央放着一个精密的计时装置——TSC(Time Stamp Counter)。它不像 jiffies 那样靠外部时钟驱动,而是直接读取 CPU 内部的振荡计数器。
"TSC 是最精确的时间源,"老者的语气变得严肃,"它以 CPU 的主频计数——如果 CPU 是 3GHz,TSC 每秒跳 30 亿次。没有中断开销,没有上下文切换,一条 RDTSC 指令就能读取。"
林小源靠近 TSC 装置,看到计数器的数字以肉眼无法分辨的速度飞速跳动。那种精度让他感到一阵眩晕——纳秒级的时间,在这里就像流水一样清晰可见。
"但 TSC 有一个问题,"老者压低声音,"早期的 CPU 在省电模式下会降低 TSC 的频率——计时就变慢了。还有多核 CPU 的 TSC 可能不同步。现代 CPU 已经修复了这些问题,但你得知道它的历史。"
林小源退后一步。面前有三层时间:jiffies 粗犷而可靠,像心跳一样稳定;hrtimer 精确而灵活,像秒表一样精准;TSC 极致而危险,像原子钟一样精密。
"什么时候用哪个?"林小源问。
老者微笑着说:"超时用 jiffies,睡眠用 hrtimer,性能测量用 TSC。选错了精度,要么浪费性能,要么丢失精度。时间管理,从来都是权衡。"
选错了精度,要么浪费性能,要么丢失精度——时间管理,从来都是权衡。
道藏笔记
内核启示
内核有三层时间精度,各管各的事。jiffies 是最粗的一层,每秒跳 HZ 次(通常 1000 次),超时、调度时间片这些够用的场景都靠它。hrtimer 是精细的一层,精度到纳秒,nanosleep()、usleep() 这类需要精确睡眠的调用走它。TSC 是最精确的一层,直接读 CPU 内部的振荡计数器,一条 RDTSC 指令搞定,性能测量用它最合适。传统 timer_list 在软中断中执行,简单可靠;hrtimer 精度高但开销也大。选哪个取决于你的场景——超时用 jiffies,睡眠用 hrtimer,性能测量用 TSC。时间管理的本质就是在精度和开销之间找平衡。
时钟之试
时钟一章中,CPU 内部用于高精度计数的时间戳计数器缩写是什么?