Skip to content

第一百五十章:时钟

合道期

涉及内核源码:

林小源走进设备山脉的一座钟楼。钟楼内部弥漫着一种均匀而持续的滴答声——每一声滴答都精确得像是用尺子量过的。

钟楼的中央悬挂着一个巨大的钟摆,钟摆每摆动一次,墙上的一组齿轮就转动一格。齿轮上刻着一个数字:jiffies。数字随着每一次滴答跳动——1024、1025、1026——永不停歇。

一个须发皆白的老者站在钟摆旁,手中拿着一根权杖,权杖顶端嵌着一颗闪烁着蓝光的晶石。

"我是系统时钟。"老者的声音和钟摆的节奏完全同步,"jiffies 是我的心跳——自系统启动以来,每秒钟跳 1000 次。HZ=1000,一毫秒一跳。"

林小源抬头看着那组齿轮。jiffies 的数字已经跳到了一个天文数字——系统从启动到现在,已经过了多少毫秒,这个数字就有多大。

"所有的时间都基于 jiffies 吗?"林小源问。

"大部分是。"老者用权杖敲了敲墙上的一个面板,面板上亮起了 "timer_list" 的字样,"这是软件定时器——你设定一个超时时间,比如 jiffies + HZ,那就是一秒钟后触发。定时器到期时,在软中断中执行回调函数。"

林小源注意到钟摆的旁边还有一座更小的钟——那座钟的刻度不是毫秒,而是纳秒。

"那是 hrtimer——高精度定时器。"老者指着小钟说,"传统定时器的精度是一毫秒,对大多数场景够用了。但如果你要同步音频、视频,或者做性能测量,一毫秒的精度远远不够。hrtimer 可以精确到纳秒。"

林小源望着那座永不停歇的钟摆,心想:整个内核的时间,都系在这根摆锤上了。


c
/*
 * 内核的时钟:
 *
 * 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");

林小源在钟楼的二层发现了一排排整齐的计时沙漏。每个沙漏上都贴着标签——有的写着 "网络超时",有的写着 "磁盘 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 内部用于高精度计数的时间戳计数器缩写是什么?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道