Linux 内核线程冻结情况分析

一、关键发现

1.1 实际情况

是的,您确实没有冻结应该被冻结的内核线程!

从日志分析:

  1. 第一阶段失败

    ini 复制代码
    [  119.065869] freeze_processes: failed to freeze tasks, error -16
    [  119.177008] PM: SUSPEND_DEBUG: suspend_freeze_processes: freeze_processes failed, error=-16
  2. 没有进入第二阶段

    • 日志中没有 看到 "freezing kernel threads""44444444 freeze_kernel_threads ENTER"
    • 说明 freeze_kernel_threads() 根本没有被调用
  3. 强制挂起

    css 复制代码
    [  119.177018] PM: SUSPEND_DEBUG: suspend_prepare: FORCE SUSPEND enabled, ignoring freeze failure

1.2 代码逻辑

kernel/power/power.h 中的代码:

c 复制代码
static inline int suspend_freeze_processes(void)
{
    error = freeze_processes();  // 第一阶段
    if (error) {
        return error;  // 如果失败,直接返回,不会调用freeze_kernel_threads()
    }
    
    error = freeze_kernel_threads();  // 第二阶段(但第一阶段失败,所以不会执行到这里)
    // ...
}

关键点

  • 如果第一阶段(freeze_processes())失败,函数会直接返回错误
  • 不会调用第二阶段freeze_kernel_threads()
  • 系统启用了FORCE SUSPEND,即使返回错误也继续挂起

二、冻结统计

2.1 从日志中看到的统计

ini 复制代码
[  118.830604] LLLLLLLL FINAL STATS: total=146, frozen=3, freezing=24, unfreezable=95, skipped=18

分析

  • total=146:总共146个任务
  • frozen=3:只有3个任务被成功冻结(用户空间进程)
  • freezing=24:24个任务正在冻结中(但最终超时失败)
  • unfreezable=95:95个任务不可冻结(有PF_NOFREEZE标志)
  • skipped=18:18个任务被跳过

2.2 被成功冻结的3个用户空间进程

从日志分析,被成功冻结的3个用户空间进程是:

PID 进程名 进入冻结时间 解冻时间 状态确认
1994 audio_server [ 105.806568] [ 119.161300] was_frozen=1
2455 sleep [ 100.207489] [ 119.174184] was_frozen=1
2456 mtop [ 99.447028] [ 119.174862] was_frozen=1

详细日志

  1. audio_server (pid 1994)

    ini 复制代码
    [  105.806568] 11111111 __refrigerator ENTER: task audio_server (pid 1994)
    [  105.809739] 11111111 __refrigerator: task audio_server (pid 1994) going to sleep, was_frozen=1
    [  119.161300] __thaw_task: woke up frozen task audio_server (pid 1994)
    [  119.165322] 11111111 __refrigerator EXIT: task audio_server (pid 1994), was_frozen=1
    • 功能:音频服务进程
    • 冻结时长:约13.35秒
  2. sleep (pid 2455)

    ini 复制代码
    [  100.207489] 11111111 __refrigerator ENTER: task sleep (pid 2455)
    [  100.208222] 11111111 __refrigerator: task sleep (pid 2455) going to sleep, was_frozen=1
    [  119.174184] __thaw_task: woke up frozen task sleep (pid 2455)
    [  119.177853] 11111111 __refrigerator EXIT: task sleep (pid 2455), was_frozen=1
    • 功能:执行sleep命令的进程
    • 冻结时长:约18.97秒
  3. mtop (pid 2456)

    ini 复制代码
    [   99.447028] 11111111 __refrigerator ENTER: task mtop (pid 2456)
    [   99.447734] 11111111 __refrigerator: task mtop (pid 2456) going to sleep, was_frozen=1
    [  119.174862] __thaw_task: woke up frozen task mtop (pid 2456)
    [  119.178822] 11111111 __refrigerator EXIT: task mtop (pid 2456), was_frozen=1
    • 功能:系统监控工具进程
    • 冻结时长:约19.73秒

说明

  • 这3个都是用户空间进程(第一阶段只冻结用户空间进程)
  • 它们都成功进入了__refrigerator()(冻结函数)
  • 解冻时都显示woke up frozen taskwas_frozen=1,确认被冻结
  • 这3个是唯一被成功冻结的任务,其他任务要么不可冻结(95个),要么冻结失败(6个),要么被跳过(18个)

2.3 没有成功冻结的内核线程

从日志分析,没有成功冻结的内核线程主要是6个冻结失败的内核线程

PID 任务名 Flags State CPU 状态
88 kswapd0 0xa20840 1 2 FAILED_FREEZE / NOT_FREEZING
239 jbd2/loop0-8 0x240040 1 2 FAILED_FREEZE / NOT_FREEZING
907 jbd2/mmcblk0p10 0x240040 1 2 FAILED_FREEZE / NOT_FREEZING
926 jbd2/mmcblk0p17 0x240040 1 2 FAILED_FREEZE / NOT_FREEZING
948 jbd2/mmcblk0p16 0x240040 1 2 FAILED_FREEZE / NOT_FREEZING
987 jbd2/mmcblk0p19 0x240040 1 3 FAILED_FREEZE / NOT_FREEZING

详细日志

  1. kswapd0 (pid 88)

    ini 复制代码
    [   98.858688] LLLLLLLL FAILED_FREEZE: kswapd0 (pid 88), flags=0xa20840, state=1, cpu=2
    [  118.843734] LLLLLLLL NOT_FREEZING: kswapd0 (pid 88), flags=0xa20840, state=1, cpu=2
    • 功能:内存交换守护线程
    • 标志分析0xa20840 = PF_KTHREAD (0x00200000) + PF_MEMALLOC (0x00000800) + PF_KSWAPD (0x00020000)
    • 状态:有PF_KTHREAD,没有PF_NOFREEZE,应该被冻结
    • 结果:尝试冻结但失败,最终没有被冻结
  2. jbd2/loop0-8 (pid 239)

    ini 复制代码
    [   98.894967] LLLLLLLL FAILED_FREEZE: jbd2/loop0-8 (pid 239), flags=0x240040, state=1, cpu=2
    [  118.844660] LLLLLLLL NOT_FREEZING: jbd2/loop0-8 (pid 239), flags=0x240040, state=1, cpu=2
    • 功能:loop设备文件系统日志线程
    • 标志分析0x240040 = PF_KTHREAD (0x00200000) + PF_FORKNOEXEC (0x00000040)
    • 状态:有PF_KTHREAD,没有PF_NOFREEZE,应该被冻结
    • 结果:尝试冻结但失败,最终没有被冻结
  3. jbd2/mmcblk0p10 (pid 907)

    ini 复制代码
    [   98.902590] LLLLLLLL FAILED_FREEZE: jbd2/mmcblk0p10 (pid 907), flags=0x240040, state=1, cpu=2
    [  118.902100] LLLLLLLL NOT_FREEZING: jbd2/mmcblk0p10 (pid 907), flags=0x240040, state=1, cpu=2
    • 功能:MMC块设备分区10的文件系统日志线程
    • 结果:尝试冻结但失败,最终没有被冻结
  4. jbd2/mmcblk0p17 (pid 926)

    ini 复制代码
    [   98.910231] LLLLLLLL FAILED_FREEZE: jbd2/mmcblk0p17 (pid 926), flags=0x240040, state=1, cpu=2
    [  118.903093] LLLLLLLL NOT_FREEZING: jbd2/mmcblk0p17 (pid 926), flags=0x240040, state=1, cpu=2
    • 功能:MMC块设备分区17的文件系统日志线程
    • 结果:尝试冻结但失败,最终没有被冻结
  5. jbd2/mmcblk0p16 (pid 948)

    ini 复制代码
    [   98.917871] LLLLLLLL FAILED_FREEZE: jbd2/mmcblk0p16 (pid 948), flags=0x240040, state=1, cpu=2
    [  118.904086] LLLLLLLL NOT_FREEZING: jbd2/mmcblk0p16 (pid 948), flags=0x240040, state=1, cpu=2
    • 功能:MMC块设备分区16的文件系统日志线程
    • 结果:尝试冻结但失败,最终没有被冻结
  6. jbd2/mmcblk0p19 (pid 987)

    ini 复制代码
    [   98.925519] LLLLLLLL FAILED_FREEZE: jbd2/mmcblk0p19 (pid 987), flags=0x240040, state=1, cpu=3
    [  118.905087] LLLLLLLL NOT_FREEZING: jbd2/mmcblk0p19 (pid 987), flags=0x240040, state=1, cpu=3
    • 功能:MMC块设备分区19的文件系统日志线程
    • 结果:尝试冻结但失败,最终没有被冻结

关键特点

  • ✅ 都是内核线程(有PF_KTHREAD标志)
  • ✅ 都没有PF_NOFREEZE标志(应该被冻结)
  • ❌ 第一阶段尝试冻结但失败(FAILED_FREEZE)
  • ❌ 第二阶段没有执行,所以它们没有被冻结
  • ❌ 最终状态显示为NOT_FREEZING(没有冻结)

为什么无法冻结

  1. kswapd0:可能正在执行内存交换操作,处于不可中断的I/O等待状态
  2. jbd2线程:可能正在写入文件系统日志,处于不可中断的磁盘I/O等待状态
  3. 状态问题:这些线程可能处于TASK_RUNNING或TASK_INTERRUPTIBLE状态,但无法响应冻结请求

影响

  • 这6个内核线程没有被冻结,处于不一致的状态
  • 系统在不完整的状态下进入深度睡眠
  • 唤醒时可能无法从不一致的状态中恢复
  • 这是导致唤醒失败的重要原因

2.4 任务分类

任务类型 数量 说明
成功冻结 3 用户空间进程(第一阶段)
冻结失败 6 kswapd0和5个jbd2线程(没有PF_NOFREEZE,但冻结失败)
不可冻结 95 有PF_NOFREEZE标志的内核线程(本来就不应该被冻结)
跳过 18 其他被跳过的任务
应该被冻结但没冻结的内核线程 6 kswapd0和5个jbd2线程(没有PF_NOFREEZE,但冻结失败)

三、应该被冻结的内核线程

3.1 判断标准

应该被冻结的内核线程

  • PF_KTHREAD 标志(是内核线程)
  • 没有 PF_NOFREEZE 标志(没有标记为不可冻结)
  • 没有 PF_FREEZER_SKIP 标志(没有被跳过)

3.2 从日志中看到的

冻结失败的内核线程(6个)

  • kswapd0 (pid 88) - flags=0xa20840(有PF_KTHREAD,没有PF_NOFREEZE)
  • jbd2/loop0-8 (pid 239) - flags=0x240040(有PF_KTHREAD,没有PF_NOFREEZE)
  • jbd2/mmcblk0p10 (pid 907) - flags=0x240040
  • jbd2/mmcblk0p17 (pid 926) - flags=0x240040
  • jbd2/mmcblk0p16 (pid 948) - flags=0x240040
  • jbd2/mmcblk0p19 (pid 987) - flags=0x240040

这些任务

  • ✅ 是内核线程(有PF_KTHREAD)
  • ✅ 没有PF_NOFREEZE标志(应该被冻结)
  • ❌ 但冻结失败(FAILED_FREEZE)
  • ❌ 第二阶段没有执行,所以它们没有被冻结

3.3 其他内核线程的情况

关键发现 :从日志分析,只有6个内核线程被尝试冻结(kswapd0和5个jbd2线程),其他所有内核线程都被标记为UNFREEZABLE。

问题:除了这6个内核线程,其他的内核线程是不需要冻结,还是说没有成功冻结?

答案:其他内核线程(95个)都是不需要冻结的(有PF_NOFREEZE标志)。

只有这6个内核线程是应该被冻结但没有成功冻结的(没有PF_NOFREEZE标志,但冻结失败)。

3.3.1 从日志统计验证

从日志 20251219_12.log 的统计信息可以看到:

ini 复制代码
FINAL STATS:
- unfreezable=95:95个任务不可冻结(有PF_NOFREEZE标志)
- failed=6:6个任务冻结失败(没有PF_NOFREEZE标志,但冻结失败)

3.3.2 UNFREEZABLE内核线程(95个)

特征

  • 所有UNFREEZABLE内核线程都有PF_NOFREEZE标志
  • 它们不需要冻结,这是正确的行为

示例

ini 复制代码
[   98.810973] LLLLLLLL UNFREEZABLE: kthreadd (pid 2), flags=0x208040, state=1
[   98.811793] LLLLLLLL UNFREEZABLE: rcu_gp (pid 3), flags=0x4208060, state=1026
[   98.812644] LLLLLLLL UNFREEZABLE: rcu_par_gp (pid 4), flags=0x4208060, state=1026
[   98.815247] LLLLLLLL UNFREEZABLE: kworker/0:0 (pid 7), flags=0x4208060, state=1026
[   98.820687] LLLLLLLL UNFREEZABLE: ksoftirqd/0 (pid 13), flags=0x4208040, state=1
[   98.822436] LLLLLLLL UNFREEZABLE: migration/0 (pid 15), flags=0x4208040, state=1
...

Flags值分析

  • 0x208040 = 0x00200000 (PF_KTHREAD) + 0x00008000 (PF_NOFREEZE) + 0x00000040 (PF_FORKNOEXEC)
  • 0x4208060 = 0x00200000 (PF_KTHREAD) + 0x00000020 (PF_WQ_WORKER) + 0x00008000 (PF_NOFREEZE) + 0x00000040 (PF_FORKNOEXEC)
  • 0x4208040 = 0x00200000 (PF_KTHREAD) + 0x00000020 (PF_WQ_WORKER) + 0x00008000 (PF_NOFREEZE)

结论 :所有这些flags值都包含PF_NOFREEZE (0x00008000),所以它们不需要冻结。

包括:kthreadd、rcu_gp、所有kworker线程、中断处理线程等

3.3.3 FAILED_FREEZE内核线程(6个)

特征

  • 这6个内核线程没有 PF_NOFREEZE标志
  • 它们应该被冻结,但冻结失败
  • 包括:kswapd0和5个jbd2线程

Flags值分析

  • 0xa20840 = 0x00200000 (PF_KTHREAD) + 0x00020000 (PF_KSWAPD) + 0x00000800 (PF_MEMALLOC) + 0x00000040 (PF_FORKNOEXEC)
    • 没有 PF_NOFREEZE (0x00008000)
  • 0x240040 = 0x00200000 (PF_KTHREAD) + 0x00000040 (PF_FORKNOEXEC)
    • 没有 PF_NOFREEZE (0x00008000)

结论 :这些flags值都不包含 PF_NOFREEZE,所以它们应该被冻结,但冻结失败了。

3.3.4 为什么只有这6个被尝试冻结?

原因 :第一阶段(freeze_processes())虽然主要冻结用户空间进程,但也会遍历所有任务,包括内核线程。

代码逻辑kernel/power/process.c):

c 复制代码
static int try_to_freeze_tasks(bool user_only)
{
    // ...
    for_each_process_thread(g, p) {
        // ...
        if (p->flags & PF_NOFREEZE) {
            unfreezable_tasks++;
            // 标记为UNFREEZABLE,跳过,不调用freeze_task
            continue;
        }
        
        // 如果没有PF_NOFREEZE标志,尝试冻结
        if (!freeze_task(p)) {
            failed_freeze_tasks++;
            // ...
        }
    }
}

说明

  • 第一阶段会遍历所有任务(包括内核线程)
  • 如果有PF_NOFREEZE标志,标记为UNFREEZABLE,跳过
  • 如果没有PF_NOFREEZE标志,尝试冻结(调用freeze_task
  • 所以,只有没有PF_NOFREEZE标志的内核线程会被尝试冻结

实际情况

  • 系统中只有6个内核线程没有PF_NOFREEZE标志(kswapd0和5个jbd2线程)
  • 这6个线程在第一阶段被尝试冻结,但都失败了
  • 其他所有内核线程都有PF_NOFREEZE标志,不需要冻结

3.3.5 是否有其他应该被冻结但没有被尝试冻结的内核线程?

答案:没有

原因

  • 从日志看,只有6个内核线程被尝试冻结(有freeze_task调用)
  • 其他所有内核线程都被标记为UNFREEZABLE(有PF_NOFREEZE标志)
  • 这说明系统中所有内核线程要么有PF_NOFREEZE标志(不需要冻结),要么没有PF_NOFREEZE标志但被尝试冻结了(虽然失败了)

关键点

  • 第一阶段虽然主要冻结用户空间进程,但也会遍历所有任务,包括内核线程
  • 所以,所有没有PF_NOFREEZE标志的内核线程在第一阶段就被尝试冻结了
  • 第二阶段主要是为了确保所有可冻结的内核线程都被冻结,但由于第一阶段已经处理了,所以第二阶段没有执行也不会遗漏

结论

  • 除了这6个冻结失败的内核线程,其他内核线程都是不需要冻结的
  • 系统中没有其他应该被冻结但没有被尝试冻结的内核线程

四、问题分析

4.1 为什么第二阶段没有执行?

原因

  1. 第一阶段(freeze_processes())超时失败(error -16 = EBUSY)
  2. suspend_freeze_processes() 在检测到第一阶段失败后直接返回错误
  3. 没有调用 freeze_kernel_threads()

代码逻辑

c 复制代码
error = freeze_processes();  // 第一阶段失败
if (error) {
    return error;  // 直接返回,不执行第二阶段
}
error = freeze_kernel_threads();  // 永远不会执行到这里

4.2 强制挂起的影响

系统行为

  • 虽然 suspend_freeze_processes() 返回错误
  • 但系统启用了 FORCE SUSPEND
  • 即使冻结失败也继续挂起
  • 结果:系统在不完整的状态下进入深度睡眠

4.3 这导致的问题

  1. 内核线程没有被冻结

    • 应该被冻结的内核线程(没有PF_NOFREEZE标志的)没有被冻结
    • 包括kswapd0和jbd2线程(虽然它们冻结失败,但至少尝试了)
  2. 状态不一致

    • 用户空间进程被冻结了(3个)
    • 内核线程没有被冻结(除了有PF_NOFREEZE标志的)
    • 系统在不一致的状态下进入深度睡眠
  3. 唤醒失败的可能原因

    • 内核线程(如kswapd0、jbd2)没有被冻结
    • 它们可能处于不一致的状态
    • 唤醒时无法从不一致的状态中恢复

五、正常流程应该是怎样的?

5.1 正常流程

scss 复制代码
suspend_freeze_processes()
  ↓
freeze_processes()  [第一阶段]
  ├─ 设置 pm_freezing = true
  ├─ 调用 try_to_freeze_tasks(true)
  └─ 冻结用户空间进程
  ↓
freeze_kernel_threads()  [第二阶段]
  ├─ 设置 pm_nosig_freezing = true
  ├─ 调用 try_to_freeze_tasks(false)
  └─ 冻结可冻结的内核线程(没有PF_NOFREEZE标志的)

5.2 实际流程(从日志看)

scss 复制代码
suspend_freeze_processes()
  ↓
freeze_processes()  [第一阶段]
  ├─ 设置 pm_freezing = true
  ├─ 调用 try_to_freeze_tasks(true)
  ├─ 冻结用户空间进程(部分成功,3个)
  └─ ❌ 超时失败(error -16)
  ↓
❌ 直接返回错误,不执行第二阶段
  ↓
FORCE SUSPEND启用,继续挂起
  ↓
系统在不完整的状态下进入深度睡眠

六、结论

6.1 关键结论

是的,您确实没有冻结应该被冻结的内核线程!

原因

  1. 第一阶段(冻结用户空间进程)失败
  2. 因为第一阶段失败,第二阶段(冻结内核线程)根本没有执行
  3. 系统启用了强制挂起,即使冻结失败也继续挂起
  4. 结果:应该被冻结的内核线程(没有PF_NOFREEZE标志的)没有被冻结

6.2 影响

  1. kswapd0和jbd2线程

    • 没有PF_NOFREEZE标志,应该被冻结
    • 第一阶段尝试冻结它们,但失败了
    • 第二阶段应该再次尝试,但第二阶段没有执行
    • 结果:它们没有被冻结
  2. 其他应该被冻结的内核线程

    • 没有PF_NOFREEZE标志
    • 第一阶段不会冻结它们(只冻结用户空间进程)
    • 第二阶段应该冻结它们,但第二阶段没有执行
    • 结果:它们也没有被冻结
  3. 系统状态

    • 只有3个用户空间进程被冻结
    • 应该被冻结的内核线程都没有被冻结
    • 系统在不一致的状态下进入深度睡眠
    • 这很可能是唤醒失败的根本原因

6.3 建议

  1. 禁用强制挂起模式

    • 如果冻结失败,应该取消挂起,而不是强制继续
    • 这样可以避免系统在不完整的状态下进入深度睡眠
  2. 优化冻结机制

    • 解决第一阶段冻结失败的问题
    • 确保能够进入第二阶段,冻结内核线程
  3. 增加调试信息

    • 记录哪些内核线程应该被冻结但没有被冻结
    • 帮助诊断问题

七、内核线程分类总结

7.1 完整分类表

类别 数量 特征 是否需要冻结 实际行为
成功冻结 3 用户空间进程 ✅ 需要 成功冻结
UNFREEZABLE 95 有PF_NOFREEZE标志 ❌ 不需要 标记为UNFREEZABLE,跳过冻结
FAILED_FREEZE 6 没有PF_NOFREEZE标志 ✅ 需要 尝试冻结但失败
跳过 18 其他被跳过的任务 - 被跳过

7.2 关键结论

  1. 其他内核线程(95个)都是不需要冻结的

    • 它们都有PF_NOFREEZE标志
    • 这是正确的行为,这些内核线程不应该被冻结
    • 包括:kthreadd、rcu_gp、所有kworker线程、中断处理线程等
  2. 只有6个内核线程是应该被冻结但没有成功冻结的

    • 它们没有PF_NOFREEZE标志
    • 应该被冻结,但冻结失败
    • 包括:kswapd0和5个jbd2线程
  3. 系统中没有其他应该被冻结但没有被尝试冻结的内核线程

    • 从日志看,只有这6个内核线程没有PF_NOFREEZE标志
    • 这6个线程都被尝试冻结了(虽然失败了)
    • 其他所有内核线程都有PF_NOFREEZE标志,不需要冻结

相关推荐
Shawn_CH2 小时前
Linux 休眠时内核线程冻结机制说明
嵌入式
才鲸嵌入式2 小时前
香山CPU(国产开源)的 SoC SDK底层程序编写,以及其它开源SoC芯片介绍
c语言·单片机·嵌入式·arm·cpu·verilog·fpga
MounRiver_Studio4 小时前
RISC-V IDE MRS2使用笔记(八):手动切换文件编码
ide·mcu·嵌入式·risc-v
大聪明-PLUS1 天前
硬件断点:它们在 Linux 中的用途和工作原理
linux·嵌入式·arm·smarc
一杯原谅绿茶2 天前
3位6脚数码管的单片机例程
stm32·嵌入式
大聪明-PLUS2 天前
如何修补 Linux 内核:完整指南
linux·嵌入式·arm·smarc
大聪明-PLUS2 天前
Docker 内部机制:深入剖析
linux·嵌入式·arm·smarc
Shawn_CH2 天前
Linux kmsg详解
嵌入式
大聪明-PLUS2 天前
常见的 Docker 问题及解决方法
linux·嵌入式·arm·smarc