nix:来自win用户的焦虑,梳理睡眠态和唤醒态的系统管理情况

结论

NixOS 的「睡眠」与 Windows 用户习惯的「睡眠」名同实异。本机走的是 s2idle(Suspend-to-Idle):CPU 停止调度、用户态整体冻结,但平台不整体断电。系统用两条钩子链把睡眠态与唤醒态变得可控、可审计:睡眠前主动断开外设与网络,唤醒后按序恢复。「合盖后热点里还显示连着」是 AP(路由器/手机)侧的客户端列表刷新滞后,不是本机还在联网 —— 日志能证明睡眠前本机主动发过解除关联帧。

睡眠态:系统做了什么

睡眠模式

内核只暴露一种挂起方式:s2idle(/sys/power/mem_sleep 仅此一项)。它与「深睡(deep/S3)」的区别在于平台不断电 —— 这正是需要钩子主动断外设的原因:若什么都不做,网卡是会在睡眠期间潜活的。

睡眠触发源(只有三种,其余刻意排除)

  • 合盖
  • 快捷键(组合键)
  • 开始菜单的「睡眠」

电源键、闲置倒计时不触发睡眠(HandlePowerKey 刻意不设,IdleAction=ignore)。合盖策略分档:外接电源与「已接坞站」都睡眠,只有「外接电源 + 合盖」这一种组合走抑制锁逻辑,电池档与低电档交给桌面环境自己的电源管理界面。

睡眠前的钩子链

各声明式模块写的 powerManagement.powerDownCommands 会被 NixOS 合并成同一个单元 sleep-actions.service 的 ExecStart。当前实际顺序:

顺序动作作用
1awake --snapshot落盘本次睡眠前的唤醒源快照,供唤醒后比对
2无线网卡卸载脚本链路 down(主动发解除关联)→ 按反依赖序卸载无线驱动模块

睡眠期间的设备状态

  • 用户态整体冻结:systemd 冻结 user.slice,任何应用都无法运行、无法联网。
  • 无线网卡不工作:驱动已被卸载,链路已 down ⇒ 物理上不通。这样刻意设计是为了绕开该网卡驱动长期存在的固件崩溃问题(睡眠前卸载可避免唤醒时固件状态损坏)。
  • 外接显示熄灭:DPMS 关断,唤醒侧另有一轮点亮补偿。
  • 网络唤醒未启用:网卡唤醒位处于禁用状态,因此睡眠期间也不存在「收特定帧唤醒」的通道。

唤醒态:系统做了什么

唤醒源(只有四类)

  • 开盖
  • 内置键盘按键
  • USB 键盘
  • USB 鼠标

电源键、定时任务(RTC/alarmtimer)、蓝牙适配器、指纹、雷雳热插拔、电源适配器拔插全部禁用,不做唤醒源。

唤醒后的钩子链

同为 sleep-actions.service,但挂在 ExecStop(单元退出时执行)。当前实际顺序:

顺序动作作用
1awake --record-wake记录本次唤醒来自哪个唤醒源,并回写清单供比对
2显示补偿脚本对 DPMS 做一次「关→开」循环,把外接屏唤醒
3无线恢复脚本重新装载驱动模块 → 等网口出现 → 等网络管理器自动重连(实测 6–8 秒)→ 超时才显式激活
4核显色深脚本等待 30 秒后重放 MST 菊花链的色深约束(看得见的多屏链路修复)

睡眠源 / 唤醒源的声明式管理

这套东西不是散落在 udev 规则里的临时配置,而是有单一数据源的三件套:

  • 清单文件(每台机器一份,文件名以本机 ESP 的 UUID 派生,因此克隆机之间天然隔离):每行一个设备,登记「设备路径 / 是否启用 / 人类可读说明」,例如「合盖开关:启用」「电源键:禁用」「USB 键鼠:启用」「蓝牙适配器:禁用」。
  • 管理命令 awake:list 打印运行时清单(含「清单里有但运行时不在位」的条目)、on/off 改运行时唤醒位、sync 才把改动写回清单文件。声明式配置与命令读的是同一份文件,没有第二份副本。
  • udev 规则做黑名单式收口:USB 子系统默认全部禁用唤醒,只有 HID 类(键盘/鼠标)才重新启用;电池事件、电源键、睡眠键、RTC 闹钟、计时器、雷雳、触摸板、电源适配器一律禁用。

此外有一条按电源状态联动的规则:交流电接入/拔出时,切换对应的合盖抑制锁单元 —— 这就是「只做插电模式」那条策略的落点。

实测证据

判据实测
挂起方式s2idle(唯一可选项)
睡眠前断开网络日志出现「解除关联帧(by local choice)」+「链路 down」+ 两个驱动模块先后卸载
用户态冻结日志出现「Successfully froze unit 'user.slice'」
钩子确实执行每次睡眠都在日志里成对出现睡眠侧与唤醒侧的动作行
唤醒后重连耗时网口 1 秒内出现;网络管理器 6–8 秒内自动重连
钩子单元归属systemctl cat sleep-actions.service:ExecStart=睡眠前脚本,ExecStop=唤醒后脚本

为什么 Windows 用户会焦虑

场景Windows 的习惯本机(NixOS)的行为
睡眠期间继续下载「现代待机」机型常能保持联网继续下载完全不能:用户态冻结 + 驱动卸载
睡眠期间收消息部分机型能收(网络保活)不能:同上;消息在唤醒后一次性补收
唤醒速度即时即时(s2idle 不落盘,唤醒很快)
唤醒后网络通常已在6–8 秒重连(驱动重载 + 扫描 + 关联)
合盖后是否真睡不一定(可能被外设或策略阻止)一定睡(合盖 = suspend,无例外)
被网络唤醒常见(网卡默认允许)不允许(唤醒位禁用,且未启用无线网络唤醒)

结论:焦虑的点其实是两件事 —— ①「睡眠还在联网」在本机不成立;②「睡眠期间不能保持联网」是本机刻意的设计,代价换来的是唤醒可靠(规避网卡固件崩溃)。两者都可改,改动方式见下节。

可配置性

想改什么改哪里代价 / 风险
睡眠期间保持无线连接注释掉无线模块里挂到 powerDownCommands/resumeCommands 的两行不推荐:该网卡驱动的固件崩溃问题可能让唤醒失败;且保活还依赖 AP 不把客户端踢下线
允许被网络唤醒(无线)启用网卡唤醒位 + 打开无线网络唤醒(magic packet)只影响「能否被唤醒」,不等于保持连接;且会牺牲当前「睡眠零网络活动」的性质
调整可用的唤醒源awake on/off <条目> 改运行时;awake sync 写回清单无 —— 这是设计内的操作方式
改变睡眠触发方式(如电源键也睡)在 logind 声明式设置里放开对应项会让合盖/快捷键之外的通路也能入睡,与当前「只有三种触发源」的约定冲突
什么都不改(当前策略)—睡眠零网络活动、唤醒可靠;代价是睡眠期间无法联网/收消息

查询命令(运行时清单)

awake list                       # 睡眠源/唤醒源运行时清单(含「清单里有但设备不在位」)
cat /sys/power/mem_sleep # 挂起方式(本机只有 s2idle)
systemctl cat sleep-actions.service # 睡眠钩子的入口(ExecStart/ExecStop)
systemctl list-dependencies sleep.target # 睡眠目标依赖了谁
journalctl -u sleep-actions -u systemd-suspend -b # 本次开机实际执行了什么
journalctl -b -2 | grep -E 'PM: suspend (entry|exit)' # 历史睡眠次数与挂起方式
cat /proc/acpi/wakeup # ACPI 侧唤醒位一览(与 awake list 互为印证)

边界

  • 本文只梳理睡眠态/唤醒态的系统管理机制,不含具体硬件的排障(外接屏、eGPU 等另有专档)。
  • 「AP 侧客户端列表刷新滞后」的具体超时取决于对端设备,未取证(无对端日志);本机侧「睡眠前主动断开」是日志确证的。
  • 无线网卡对无线网络唤醒(WoWLAN)的支持情况未复核;可确定的是其唤醒位处于禁用状态。
  • 本次为只读梳理,未改动任何配置、未触发任何睡眠/唤醒动作。