gettimeofday()
每次调用返回当前时间——底层读的是什么硬件?TSC 够稳吗?为什么
vDSO 能不进内核就读时间?
一、先看图
flowchart TD
APP[gettimeofday / clock_gettime] --> VDSO[vDSO<br/>用户态读取]
VDSO --> SEQCOUNT[seqcount_latch<br/>读 timekeeper]
SEQCOUNT --> CSRC[clocksource<br/>read 硬件计数器]
subgraph 硬件时钟
TSC[TSC<br/>rating: 300]
HPET[HPET<br/>rating: 250]
ACPI[ACPI PM<br/>rating: 200]
ARM_GT[ARM Generic Timer<br/>rating: 400]
end
CSRC --> TSC
CSRC --> HPET
CSRC --> ACPI
CSRC --> ARM_GT
classDef user fill:#3fb95022,stroke:#3fb950,color:#adbac7;
classDef hw fill:#388bfd22,stroke:#388bfd,color:#adbac7;
class APP,VDSO,SEQCOUNT user
class TSC,HPET,ACPI,ARM_GT,CSRC hw
二、clocksource 框架
struct clocksource {
u64 (*read)(struct clocksource *cs);
u32 rating; // 越高越好
const char *name;
// ...
};内核选择 rating 最高的可用 clocksource。
2.1 常见 clocksource
| 名称 | rating | 精度 | 平台 |
|---|---|---|---|
| TSC | 300 | ns | x86 |
| HPET | 250 | ns | x86(慢) |
| ACPI PM | 200 | μs | x86(最慢) |
| arch_timer | 400 | ns | ARM |
| KVM clock | 400 | ns | KVM guest |
cat /sys/devices/system/clocksource/clocksource0/available_clocksource
cat /sys/devices/system/clocksource/clocksource0/current_clocksource三、TSC 的稳定性
3.1 Invariant TSC
现代 CPU(Sandy Bridge+)标记 constant_tsc +
nonstop_tsc:
- 频率不随 P-state 变化
- 深度 C-state 不停止
3.2 Unstable TSC
旧 CPU 或某些虚拟化环境 → TSC 不稳定 → 内核 watchdog 检测到后降级到 HPET/ACPI PM。
clocksource: Marking clocksource 'tsc' as unstable because the skew is too large
3.3 TSC 同步
多 socket 系统 → 各 socket TSC 可能不同步 → 内核检查 TSC 偏差。
四、clockevents
clocksource 是读时间;clockevents 是编程定时器。
struct clock_event_device {
void (*event_handler)(struct clock_event_device *);
int (*set_next_event)(unsigned long, struct clock_event_device *);
enum clock_event_state state_use_accessors;
// ...
};hrtimer 用 clockevents 编程下一个到期中断。
五、vDSO
Virtual Dynamic Shared Object:内核把一小段代码和数据映射到每个进程地址空间。
clock_gettime(CLOCK_MONOTONIC) → 不进内核 →
vDSO 读 seqcount + TSC → 返回。
# 查看 vDSO
ldd /bin/ls | grep vdso
# linux-vdso.so.15.1 性能
| 方式 | 延迟 |
|---|---|
| syscall clock_gettime | ~200ns |
| vDSO clock_gettime | ~20ns |
10 倍差距。
六、PTP/PHC
精确时间协议(Precision Time Protocol):
# 硬件时间戳
ethtool -T eth0 | grep -i ptp
# 配置 ptp4l
ptp4l -i eth0 -mPHC(PTP Hardware Clock)提供独立的硬件时钟 → 纳秒级网络时间同步。
七、time namespace(5.6+)
容器可以有独立的时间偏移:
unshare(CLONE_NEWTIME);
// 修改 /proc/self/timens_offsets用途:容器迁移时保持时间连续性。
八、Watchdog
内核定期用次优 clocksource 校验主 clocksource:
clocksource: timekeeping watchdog on CPU0
偏差过大 → 标记为 unstable → 切换。
九、观察
# 当前时钟源
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
# TSC 特性
grep -E "constant_tsc|nonstop_tsc" /proc/cpuinfo
# clockevents
cat /sys/devices/system/clockevents/clockevent0/current_device
# vDSO 性能
perf stat -e 'syscalls:sys_enter_clock_gettime' -- ./benchmark十、小结
- clocksource 按 rating 选最佳;TSC 是 x86 首选
- TSC 需要 invariant + nonstop 才可靠
- vDSO 让 gettimeofday 零 syscall → 20ns
- PTP/PHC 提供硬件纳秒同步
- clockevents 编程硬件定时器中断
参考文献
kernel/time/clocksource.carch/x86/kernel/tsc.cDocumentation/timers/- 分布式百科-06-physical-clocks(交叉引用)
工具
/sys/devices/system/clocksource/ethtool -T- ptp4l / phc2sys
- perf
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】vDSO
vDSO 把 gettimeofday/clock_gettime 搬到用户态——零 syscall 读时间。本文讲 vDSO 原理、vvar 共享页、getrandom vDSO、x86/arm64 差异、vsyscall 退役、time namespace 支持。
【操作系统百科】系统调用 ABI:x86_64 / arm64 / riscv / Windows NT 对照
系统调用是 OS 最稳定的接口。本文拆解 Linux syscall 的参数寄存器约定、返回值规范(负 errno 与 2-value ABI)、x86_64 SYSCALL、arm64 SVC、RISC-V ECALL、Windows NT 的 int 2e/syscall/SYSENTER 历史;说明为什么 Linux 承诺 \"don't break userspace\"、什么东西算 syscall ABI、vDSO 如何用共享内存加速、Go/musl/glibc 各自怎么实现 syscall stub。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。