kthreadd卡在D状态基本可排除自身问题,因其仅负责fork内核线程,卡住实为等待子线程(如kswapd0)完成,根源在I/O、内存回收或cgroup限流等下游资源死锁。
![进程卡在 d 状态 [kthreadd] 或 [kswapd0] 的深层原因定位](https://img.php.cn/upload/article/001/242/473/176924028391486.jpeg)
为什么 kthreadd 卡在 D 状态基本可以排除进程自身问题
kthreadd 是内核线程的“父进程”,本身不执行业务逻辑,只负责 fork() 出其他内核线程(比如 kswapd0、khungtaskd)。它卡在 D(uninterruptible sleep)状态,几乎总是因为某个它刚 fork 出的子线程卡住了,导致 kthreadd 在 wait_event() 或类似同步点上被阻塞——不是它自己慢,而是它在等别人。
实操建议:
- 先用
ps -eo pid,ppid,comm,wchan:20,state | grep 'D.*kthreadd'查看哪个子线程在等什么内核函数(wchan列) - 再用
cat /proc/看该子线程完整调用栈,重点关注最顶层是否落在/stack __io_wait_event、sleep_on_bit、mem_cgroup_wait等 I/O 或内存路径上 - 别急着重启,
kthreadd卡住往往是下游资源(如存储、cgroup 限流、page cache 回写)已死锁的表象
kswapd0 长期 D 状态通常指向内存回收路径受阻
kswapd0 的职责是异步回收内存页,D 状态说明它正卡在某个不可中断的等待中,常见于:设备 I/O 延迟过高、底层块设备无响应、或 cgroup 内存压力下被强制 throttle。和用户态进程不同,它不会因信号中断,所以一旦卡住,整个内存回收就停摆。
实操建议:
- 检查
/proc/vmstat中pgmajfault、pgpgin、pgpgout是否持续飙升,配合vmstat 1观察si/so(swap in/out)是否长期非零 - 用
cat /proc/看栈顶:若停在/stack blk_mq_get_tag或submit_bio,说明块层卡住;若停在try_to_unmap或shrink_inactive_list,可能是特定 page 类型(如 dirty file-backed page)无法释放 - 临时验证:运行
echo 1 > /proc/sys/vm/drop_caches(仅清 pagecache)看是否能唤醒kswapd0;若不能,大概率是底层存储或驱动问题
定位真正瓶颈:从 /proc/buddyinfo 和 /sys/kernel/debug/block/*/io_stats 入手
D 状态本身只是现象,真正瓶颈往往藏在内存分配器或 I/O 子系统。比如 kswapd0 卡在 balance_pgdat,但实际是因为 buddy allocator 无法满足高阶内存请求(order-3+),进而反复触发更激进的回收,形成恶性循环。
实操建议:
- 运行
cat /proc/buddyinfo,关注高阶(order-4 及以上)空闲页是否为 0;若持续为 0,说明内存碎片严重,kswapd0会不断尝试 compaction,而 compaction 又依赖 I/O 完成,容易卡死 - 检查对应块设备的 debugfs 统计:
cat /sys/kernel/debug/block/,观察/io_stats in_flight是否长期 > 0,且io_ticks不增长——这是设备无响应的强信号 - 若使用 NVMe,注意
dmesg中是否有nvme nvme0: timeout或reset controller日志;这些会直接让kswapd0卡在nvme_queue_rq
容易被忽略的三个硬性条件
很多排查止步于“看起来像磁盘慢”,但以下三点常被跳过,却直接决定是否真能复现或缓解:
- 确认是否启用了
memory.max(cgroup v2)或memory.limit_in_bytes(v1):当容器/进程内存被严格限制,且脏页比例超阈值时,kswapd0会被强制同步回写,极易卡在writeback路径 - 检查是否挂载了
nobarrier或noatime的 ext4/xfs:某些旧内核版本在禁用 barrier 下遇到突发 I/O 错误时,会卡在__wait_on_bit_lock等待 page lock,而非报错退出 - 确认内核是否打了特定补丁:例如 5.4.0-105-generic(Ubuntu)存在一个
kswapd在 memcg under high pressure 下死锁的 bug,需升级到 5.4.0-107+
卡在 D 状态的内核线程本身不提供调试接口,所有线索都来自栈、统计文件和周边子系统状态。最耗时间的往往不是找“哪一行代码卡住”,而是确认“哪一层资源彻底不可达”。










