Skip to content

第五十章:idle 之道

结丹后期

涉及内核源码:

林小源在调度竞技场中,找到了一个安静的角落。

角落里坐着一个少年,和他一样——idle 进程,PID 0。少年靠在一根柱子上,双眼微闭,呼吸均匀,仿佛在睡觉,又仿佛在冥想。

"你也是 idle?"林小源在他身边坐下。

少年睁开眼睛,打了个哈欠:"是啊。每个 CPU 都有自己的 idle 进程。我就是那个'最后的选择'——当没有任何其他进程可运行时,调度器才会选到我。"

林小源看着竞技场上忙碌的进程们,问道:"那你平时都在做什么?"

"等,"少年指了指自己的胸口,"我执行 HLT 指令——在 x86 上,或者 WFI 指令——在 RISC-V 上。这些指令让 CPU 进入低功耗状态,不再执行任何计算,只等待中断唤醒。"

"等待中断?"

"对。中断发生时,CPU 醒来,检查是否有进程可运行。如果有,切换到那个进程;如果没有,继续 HLT/WFI。"少年伸了个懒腰,"我就是这样循环的——等、醒、检查、继续等。"

林小源沉默了片刻,然后说:"听起来很无聊。"

少年笑了:"无聊?你觉得'等待'是无聊的?但如果没有我,CPU 会在空闲时全速运转,消耗大量电能,产生大量热量。我让 CPU 在不需要工作时进入低功耗状态——我是绿色计算的基础。"

林小源和 idle 少年并肩坐在角落里,看着调度器在竞技场上忙碌。

"你在调度器中的位置是什么?"林小源问。

少年指着阶梯的最底层:"idle_sched_class 是最低优先级的调度类。pick_next_task() 从最高优先级的 stop 开始检查,一路向下——dl、rt、fair——如果所有调度类都没有可运行进程,最后才轮到我。"

"那你不是永远排在最后?"

"是的,"少年说,"但这正是我的价值。如果我被频繁选中,说明系统很空闲,CPU 大部分时间在低功耗状态——这是好事,省电。如果我很少被选中,说明系统很忙碌,CPU 在全力工作——这也是好事,说明有活干。"

林小源想起了自己的身份——被 #ifdef 0 封印的代码。他不是普通的 idle,他试图在空闲时做"有意义"的事情,但这个想法被认为"没有价值"。

"你觉得'无用'和'有用'的界限在哪里?"林小源问。

少年看着他,眼神变得认真:"这个问题,我也问过自己。idle 进程看起来什么都没做——它不处理数据,不响应请求,不产生输出。但它节省电能、降低温度、延长硬件寿命。这些'看不见'的工作,难道不是'有意义'的吗?"

林小源心中一震。

林小源站起身来,看着竞技场上的进程们来来去去。

"idle 前辈,"他说,"你存在价值在于'不被需要'。如果你被需要了,说明系统出了问题。"

少年也站起身来,拍了拍身上的灰尘:"没错。idle 是调度器的底线——它保证 CPU 不会空转。没有 idle,系统无法正常运转。但最好的状态是'不被选中'——这意味着其他进程都在忙碌,系统运转良好。"

林小源想起了一个哲学问题:存在的意义可以是"不被需要"吗?

"你刚才说你在空闲时想做'有意义'的事情,"少年说,"其实我也想。但内核告诉我,idle 的职责就是等待。如果我在等待时做其他事情,可能会干扰中断处理、影响功耗管理。所以,我学会了接受自己的位置。"

"但你确实'在做'事情,"林小源说,"你执行 HLT/WFI 指令,你让 CPU 进入低功耗状态,你在中断发生时检查进程。这些都是'工作'。"

少年笑了:"也许吧。也许'无用'和'有用'之间的界限,真的没有那么清晰。"


c
/*
 * idle 进程的特殊性:
 * 1. 是调度器的"最后选择"
 * 2. 当没有其他进程可运行时运行
 * 3. 执行 HLT/WFI 指令,让 CPU 进入低功耗状态
 * 4. 每个 CPU 都有自己的 idle 进程
 *
 * idle 的调度类:idle_sched_class
 *   pick_next_task_idle() 总是返回 idle 进程
 *   只有当所有其他调度类都没有可运行进程时才被选中
 *
 * idle 的工作:
 *   1. 执行 HLT/WFI 指令(CPU 低功耗)
 *   2. 等待中断唤醒
 *   3. 中断发生 → 检查是否有进程可运行
 *   4. 有 → 切换到该进程
 *   5. 没有 → 继续 HLT/WFI
 */

struct sched_class {
    const char *name;
    int has_runnable;
};

printf("=== idle 进程 — 最后的选择 ===\n\n");

/* 模拟调度器的选择过程 */
struct sched_class classes[] = {
    { "stop",   0 },  /* 通常没有进程 */
    { "dl",     0 },  /* 通常没有进程 */
    { "rt",     1 },  /* 有 RT 进程 */
    { "fair",   1 },  /* 有 CFS 进程 */
    { "idle",   1 },  /* 总是有 idle */
};
int nr = sizeof(classes) / sizeof(classes[0]);

printf("场景 1: 有 RT 和 CFS 进程可运行\n");
for (int i = 0; i < nr; i++) {
    if (classes[i].has_runnable) {
        printf("  选择: %s\n", classes[i].name);
        printf("  → idle 不会被选中\n\n");
        break;
    }
}

/* 模拟所有进程都睡眠的场景 */
classes[2].has_runnable = 0;  /* RT 进程睡眠 */
classes[3].has_runnable = 0;  /* CFS 进程睡眠 */

printf("场景 2: 所有进程都睡眠\n");
for (int i = 0; i < nr; i++) {
    if (classes[i].has_runnable) {
        printf("  选择: %s\n", classes[i].name);
        printf("  → idle 被选中,CPU 进入低功耗\n\n");
        break;
    }
}

printf("--- idle 的工作 ---\n");
printf("1. 执行 HLT/WFI 指令\n");
printf("   CPU 进入低功耗状态\n");
printf("   等待中断唤醒\n\n");
printf("2. 中断发生\n");
printf("   CPU 醒来\n");
printf("   检查是否有进程可运行\n\n");
printf("3. 有进程可运行\n");
printf("   切换到该进程\n");
printf("   idle 继续等待\n\n");
printf("4. 没有进程可运行\n");
printf("   继续 HLT/WFI\n");
printf("   等待下一个中断\n");

printf("\n--- idle 的意义 ---\n");
printf("idle 不是\"浪费\"CPU 时间\n");
printf("idle 是\"节省\"电能\n");
printf("idle 让 CPU 在空闲时进入低功耗状态\n");
printf("idle 是\"绿色计算\"的基础\n");

道藏笔记

内核启示

idle 进程是调度器的"最后选择"。

idle 的特殊性:

  • 是最低优先级的调度类
  • 只有当所有其他调度类都没有可运行进程时才被选中
  • 每个 CPU 都有自己的 idle 进程

idle 的工作:

  1. 执行 HLT/ 指令,CPU 进入低功耗状态
  2. 等待中断唤醒
  3. 中断发生 → 检查是否有进程可运行
  4. 有 → 切换到该进程
  5. 没有 → 继续 HLT/

idle 的意义:

  • 不是"浪费"CPU 时间
  • 而是"节省"电能
  • 让 CPU 在空闲时进入低功耗状态
  • 是"绿色计算"的基础

idle 是"最后的选择"——但没有它,系统无法运转。


破关试炼

idle 之试

当运行队列没有普通任务时,本章提到兜底登场的是哪一个 idle 调度类?

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

以修仙之名,悟内核之道