第44期 | 问个时间,也要进一趟内核?
一、内核把答案,贴在了门上
上一期说,门一旦打开就不许再关——ABI 是铁律。可铁律有代价:每次进门,都要穿越一道特权边界,存寄存器、切页表、再恢复。而程序里问得最勤的,偏偏是最不值得进门的一件事:现在几点了。日志打时间戳、超时判断、性能打点,一秒钟能问上几千次。man 手册里写得直白:有些系统调用被用得太频繁,频繁到"能主导整体性能",贵就贵在调用频率,和"退出用户态、进入内核的上下文切换开销"上¹。
2002 年,x86-64 的 2.5.6 内核给了第一版答案,叫 vsyscall²:内核在 0xffffffffff600000 开头的一页固定地址上,放了 gettimeofday、time、getcpu 的"免检版",用户态直接跳进去执行,一次门都不用进。快是快了,可固定地址是攻击者的乐园——那页代码躺在人人都知道的地址上,成了 ROP 链现成的跳板³。
2011 年 6 月,Andy Lutomirski 动手拆这颗钉子:一组补丁把 vsyscall 页的代码清空,只留一条 trap 指令³。从此,误闯 vsyscall 的老程序会 trap 进内核、按普通系统调用仿真执行——比正常进门还慢,但 ABI 不破。连 Linus 都只挑了补丁里一个配置项的名字(UNSAFE 太政治化,改叫 LEGACY),没人反对这件事本身:老二进制必须能跑,慢点也行。第 43 期的铁律,在这里续上了。
真正的接班人叫 vDSO(virtual dynamic shared object):2007 年随 2.6.23 登上 x86-64²。不再是固定地址的一页代码,而是一个随 ASLR 漂移的小型共享库,内核把它映射进每个进程的地址空间¹。跳板不复存在。
vDSO 的妙处不在"免检",而在分工。内核把时间数据放在一块共享内存页里持续维护,vDSO 导出的 __vdso_gettimeofday、__vdso_clock_gettime 这些函数¹全在用户态跑:读 CPU 的时间戳计数,套共享页里的换算参数,自己算出"现在几点"——全程不穿越,一次门都不进。
2018 年 Meltdown 爆发,KPTI 给每次穿越又加了一道页表切换的税⁴,vDSO 这张"告示"的价值反而更高了。
这像古代城楼上的大钟:全城人看时间不用进衙门问,抬头就行。内核在每个进程的地址空间里,都挂了这样一口钟。
二、告示板,代替传话人
如果操作系统的第一批用户是智能体,"敲门传话"可能根本不会是默认动作。一个 agent 每秒要读上千次世界状态——时间、负载、传感器影子——每次都穿越边界去"问",是前 AI 时代的笨办法。
推演:内核维护若干块"告示板"——只读的共享真值页。时间一板,调度状态一板,设备影子一板。读世界免费,直接看板;改世界才收费,必须进门。凡有副作用、需仲裁的,才走系统调用。这其实是把"请求—应答"换成了"发布—订阅"。
维纳在《Cybernetics》(1948)里讲的反馈回路是"感知—比较—执行"⁵(思想化用)。vDSO 把这个回路砍掉了一截:感知可以直接发生,不必每次都经过"问"。控制论的老话,在这里长出了新枝。
照见那根看不见的线:门的代价,平时是高频调用者在默默买单。vDSO 把"读"从"问"里摘了出来——AI OS 里也该如此:读世界应该是免费的,改世界才收费。这是第 58 章"Agent 时代的 /proc"要接着挖的伏笔:给智能体看的系统状态,不该是一扇门,该是一面墙的告示板。
三、三条命令,看见那口钟
手边任何一台 Linux 机器,今晚就能验证。
第一,看钟挂在哪:
grep -E 'vdso|vsyscall' /proc/self/maps
每个进程的地址空间里都躺着 [vdso]——进程一生下来,钟就挂在那儿了。[vsyscall] 那页还在,但里面只剩 trap 指令,是块纪念碑。
第二,看"问了一千万次,门一次没开":写 5 行 C,循环调用 gettimeofday 一千万次。time 测总耗时,快得离谱;再 strace -c 数它到底进了几次内核——gettimeofday 一栏是 0。一千万次调用,零穿越,时间全在用户态算出来的。
第三,落到普遍场景。做设备接入或智能体平台时,agent 高频轮询设备状态,别让每次轮询都走一次"门"(一次 RPC、一次系统调用)。学 vDSO 的做法:mmap 一块共享内存做状态板,写端用 seqlock 保护,读端在用户态直接读——读状态零穿越,写状态才走仲裁。视频 AI 平台里,研判层每秒读上千次设备与告警状态;企业私有智能中心里,智能体轮询任务状态——都吃这一套。
vDSO 没有发明新原理:共享内存、函数指针,都是老把戏。它赢在把"读时间"这个最高频的动作工程化了——杨振宁说的中间层,"先实际后原理"。
范式之眼补一句:vDSO 省的是 CPU 进出门的税。真正的错位在另一边:GPU 上的时间与状态,根本不在这扇门后面。那笔税还没人动过——第 49 章、第 61 章再算。
觉 然 小 注
门开得再快,也快不过把答案贴在门上。所谓系统调用,即非系统调用,是名系统调用——化用《金刚经》"如来说世界,即非世界,是名世界"⁶。
引 用 与 出 处
- [1] Linux man-pages 项目. vdso(7)("Why does the vDSO exist at all?":高频调用与上下文切换开销;内核将 vDSO 映射进所有用户进程地址空间;导出 __vdso_clock_gettime 等函数). https://manpages.debian.org/bullseye/manpages/vdso.7.en.html
- [2] The vDSO on arm64. Linux Plumbers Conference 2016, slides(vsyscall/vDSO 分架构历史表:x86-64 vsyscall 始于 2.5.6/2002,x86-64 vDSO 始于 2.6.23/2007). https://blog.linuxplumbersconf.org/2016/ocw/system/presentations/3711/original/LPC_vDSO.pdf
- [3] Corbet, J. On vsyscalls and the vDSO. LWN.net, 2011(固定地址 0xffffffffff600n00 的安全问题;Lutomirski 2011 年 6 月补丁系列;vsyscall 改仿真执行,ABI 不破). https://lwn.net/Articles/446528/
- [4] Lipp, M. et al. Meltdown: Reading Kernel Memory from User Space. 2018. https://meltdownattack.com/meltdown.pdf
- [5] Wiener, N. Cybernetics: Or Control and Communication in the Animal and the Machine. MIT Press, 1948. (思想化用)
- [6] 鸠摩罗什 译. 《金刚般若波罗蜜经》第十三分"如法受持分". 后秦弘始年间(约402年)译本. ("如来说世界,即非世界,是名世界"一句,思想化用)
· · ·