Linux 里的 sleep(2) 背后是什么?
进程调用 sleep,内核把它标记为"阻塞",让出 CPU 给其他进程,等时间到了再唤醒它。实现这个功能需要两个组件配合:定时器(知道时间到了)+ 调度器(切换进程)。
好消息是这两个我们都有了:PIT 100Hz 时钟 + round-robin 调度器。这一章只需要把它们接起来。
核心思路
SYS_SLEEP(ms)
→ sleep_until = pit_get_ticks() + ms/10
→ state = PROC_BLOCKED
IRQ0 handler(每 10ms)
→ pit_tick() // ticks++
→ timer_tick() // 扫描所有进程,到期则唤醒
→ sched_tick() // 调度下一个 READY 进程
进程睡觉时设好"闹钟"(sleep_until),然后自己进入 PROC_BLOCKED。
每次时钟中断,timer_tick 扫一遍所有进程,到期的改回 PROC_READY,下次调度就能运行了。
实现
1. process_t 加一个字段
typedef struct {
// ... 原有字段
uint64_t sleep_until; // 睡到哪个 tick,0 表示没在睡
} process_t;
2. timer.c
void timer_tick(void) {
uint64_t now = pit_get_ticks();
for (int i = 0; i < MAX_PROCS; i++) {
if (procs[i].state == PROC_BLOCKED && procs[i].sleep_until > 0) {
if (now >= procs[i].sleep_until) {
procs[i].sleep_until = 0;
procs[i].state = PROC_READY;
}
}
}
}
逻辑极简:遍历,到期,唤醒。
3. isr.c 接入
if (irq == 0) {
pit_tick();
timer_tick(); // ← 新增
sched_tick(regs);
}
顺序很重要:先更新 ticks,再检查超时,最后做调度。
4. SYS_SLEEP 实现
case SYS_SLEEP: {
uint64_t ms = a1;
uint64_t ticks = (ms * 100) / 1000; // 100Hz,每 tick 10ms
if (ticks == 0) ticks = 1;
current->sleep_until = pit_get_ticks() + ticks;
current->state = PROC_BLOCKED;
return 0;
}
syscall 返回后,sched_tick 会发现当前进程是 PROC_BLOCKED(不是 PROC_RUNNING),
直接跳过,切到其他进程。睡眠就这样发生了。
测试
用户程序很简单:
mov rax, SYS_WRITE
lea rdi, [rel msg_before] ; "going to sleep for 2s..."
syscall
mov rax, SYS_SLEEP
mov rdi, 2000 ; 2000ms
syscall
mov rax, SYS_WRITE
lea rdi, [rel msg_after] ; "woke up!"
syscall
运行结果:
sleep test: going to sleep for 2s...
sleep test: woke up!
两条消息之间有明显的 ~2 秒延迟。
几个细节
sleep_until = 0 的特殊语义
用 0 表示"没在睡",避免进程刚创建(sleep_until 默认 0)时被 timer_tick 误唤醒。
与 proc_wait 共用 PROC_BLOCKED
proc_wait 等子进程时也用 PROC_BLOCKED,靠子进程退出时主动唤醒父进程。
两者区分靠 sleep_until:
sleep_until > 0→ 定时睡眠,timer_tick 唤醒sleep_until == 0→ 等待事件(wait),事件发生时手动唤醒
精度
PIT 100Hz,最小粒度 10ms。sleep(1ms) 实际最多等 10ms。
如果需要更高精度,可以把 PIT 频率调高,或改用 HPET/APIC timer,这里就不搞了。
小结
sleep 的本质是:把自己标记为不可调度,等到某个条件满足再变回可调度。 这里条件是"时间到了",其他情况(等 IO、等信号量)机制完全一样。
至此我们的 OS 有了:进程调度、系统调用、TTY、文件系统、管道、信号、poll、字符设备、定时睡眠。 下一章继续往前走。