[TOC]
问题描述:
调试linux的休眠与唤醒日志
添加打印日志信息分析步骤
第1步:打开相关内核配置
CONFIG_PM_DEBUG=y
CONFIG_PM_ADVANCED_DEBUG=y
CONFIG_PM_TEST_SUSPEND=y
CONFIG_PM_SLEEP_DEBUG=y第2步:使能相关设置
echo N > /sys/module/printk/parameters/console_suspend
echo 1 > /sys/power/pm_print_times
echo 8 > /proc/sys/kernel/printkecho N > /sys/module/printk/parameters/console_suspend
作用:控制系统休眠 (suspend) 期间控制台是否输出日志
说明:N表示禁用,Y表示启用。设置为N可以在系统休眠时停止控制台日志输出,减少休眠 / 唤醒过程中的干扰
echo 1 > /sys/power/pm_print_times
作用:启用电源管理操作的时间记录功能
说明:设置为1后,系统会记录各种电源管理操作(如休眠、唤醒)的时间点和持续时长,有助于调试电源管理相关问题,日志通常会输出到内核环缓冲区
echo 8 > /proc/sys/kernel/printk
作用:调整内核控制台日志级别
说明:printk的参数值范围是 0-7(数值越小优先级越高),这里设置为 8 比较特殊,实际上会使用默认的日志级别配置。该参数控制哪些级别的内核消息会被输出到控制台,常用于调试时调整日志详细程度
echo 8 > /proc/sys/kernel/printk 或者 dmesg -n 8 修改的是第一个参数
cat /proc/sys/kernel/printk
4 4 1 7
这四个数字分别代表以下含义:
第一个数字(控制台日志级别):
控制哪些级别的内核消息会被打印到控制台。只有消息级别小于该值时,才会显示在控制台。
例如:值为 4 时,只会显示级别 0-3 的消息(紧急、警报、严重错误、错误)。
第二个数字(默认消息级别):
当内核消息未明确指定级别时,使用的默认级别。
例如:值为 4 时,未指定级别的消息会被视为级别 4(警告)。
第三个数字(最低控制台日志级别):
用于设置控制台日志级别的下限(最小值)。
用户不能将第一个数字设置得比该值更小(更严格)。例如:值为 1 时,控制台日志级别不能设置为 0。
第四个数字(默认控制台日志级别):
内核启动时的默认控制台日志级别,也作为恢复默认设置时的参考值。
- 可以查看休眠唤醒的时间
- 查看休眠唤醒的结果
- 休眠唤醒单元测试
代码片段
一个实际案例:在实际休眠唤醒时,使用了wait_event_interruptible函数,然后在休眠过程中,来了信号导致线程被唤醒,操作了重要的资源,导致休眠失败。然后用wait_event_freezable函数替代解决问题
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/suspend.h>
// 等待队列和条件变量
static wait_queue_head_t my_waitq;
static int condition = 0;
// 关键资源锁(休眠流程需要访问)
static DEFINE_MUTEX(suspend_mutex);
// 等待线程:使用wait_event_interruptible
static int waiting_thread(void *data)
{
// printk(KERN_INFO "Waiting thread started (PID: %d)\n", task_pid_nr(current));
//默认内核线程是不被冻结的
if (current->flags & PF_NOFREEZE)
printk(KERN_INFO "This thread is NOT freezable\n");
else
printk(KERN_INFO "This thread IS freezable\n");
//告诉内核可以该线程可以被冻结,需要配合wait_event_freezable使用
set_freezable();
//current->flags |= PF_NOFREEZE; //直接操作设置不可以被冻结
//current->flags &= ~PF_NOFREEZE; //直接操作设置可以被冻结
if (current->flags & PF_NOFREEZE)
printk(KERN_INFO "set This thread is NOT freezable\n");
else
printk(KERN_INFO "set This thread IS freezable\n");
while (!kthread_should_stop()) {
/*
在实际场景中遇到过这种情况: wait_event_interruptible,然后此时正好被唤醒,操作了
重要的资源(关掉了sram的时钟,导致在休眠过程中出现了异常)
所以使用wait_event_interruptible还是wait_event_interruptible,主要考虑下面的情况:
简单说就是:你写的内核线程,要不要用 wait_event_freezable,核心看一个事儿 ——这线程会不会在系统休眠的时候 “添乱”。系统休眠要求所有 “关键角色” 都得 “暂停干活、松手资源”,不然休眠就卡那了。
先搞懂一个前提:系统休眠怕啥?
系统休眠时,内核会喊一句:“所有人停手!资源都放了!”(这就是 “冻结”)。如果有线程不听 —— 要么还在拿着锁 / 硬件资源不放,要么明明能停却不停 —— 休眠就会卡住,甚至直接失败(比如等半天这线程还没停,超时了)。
wait_event_freezable 就是帮线程 “听话” 的工具:让线程在等事儿的时候,顺便能响应 “冻结” 指令,乖乖暂停。
啥时候必须用?—— 线程会 “占着茅坑不拉屎”
如果你的线程符合下面情况,必须用,不然休眠会崩:
线程会 “长期摸鱼等事儿”:比如线程没啥事干,就挂在那等一个信号(比如等设备发消息、等数据到),一挂可能挂很久。
线程手里有 “关键东西”:比如拿着设备的锁、占着 IO 资源(像读写硬盘、控制传感器)。
这种线程要是不用 wait_event_freezable,而是用别的等事儿函数(比如 wait_event_interruptible),就会像 “戴着耳机听不见指挥”—— 内核喊 “冻结” 它不理,一直保持 “等事儿” 的状态。要是手里还拿着资源,直接就把休眠卡死后;就算没拿资源,内核等它半天没反应,也会超时失败。
比如:一个监控设备状态的线程,平时就挂着等设备有动静,这种就必须用 wait_event_freezable,不然休眠时它还在那等,内核急死也没用。
啥时候不用?—— 线程 “不添乱”
如果你的线程是下面这些情况,不用也没事,不会影响休眠:
线程是 “临时工”:干一票就走,比如启动后快速处理个数据,几毫秒就退出了。休眠的时候它早没了,根本不用管。
线程 “自己会看眼色”:虽然一直在跑,但会主动检查 “要不要冻结”。比如线程在循环干活,每次循环完都问一句 “内核让我停吗?”(用 try_to_freeze() 函数),这种就算不用 wait_event_freezable,也会乖乖停。
线程是 “特殊豁免户”:明确告诉内核 “我不能停”(用 set_freezable(false)),而且确实不占关键资源(比如 watchdog 线程,休眠时也得看着系统,不能停)。这种情况极少,一般用不到。
总结:一句话判断
如果你的线程会 “长期挂着等事儿”(不是干一会儿就走),那就必须用 wait_event_freezable;反之,不用也行。
*/
// 阻塞等待:条件满足或被唤醒,如果在等待过程中,系统进行了休眠,则会不冻结线程,如果有wake_up信号过来,会继续往下执行
//wait_event_interruptible(my_waitq, condition || kthread_should_stop());
//如果在等待过程中,系统进行了休眠,则会冻结线程,即使有wake_up信号过来,也不会往下执行
wait_event_freezable(my_waitq, condition || kthread_should_stop());
printk(KERN_INFO "wait_event_interruptible done\n");
// 2. 检查是否需要退出
if (kthread_should_stop())
break;
// 3. 关键修复:检测系统是否正在休眠,若正在休眠则重新进入等待
if (freezing(current)) {
printk(KERN_INFO "Thread: System is freezing, re-enter wait...\n");
condition = 0; // 重置条件
continue; // 重新进入等待,避免持有锁
}
// 先获取锁处理必要操作
mutex_lock(&suspend_mutex);
// 若被唤醒(非停止)
if (!kthread_should_stop()) {
// 执行后续操作(持有锁)
// msleep(2000); // 模拟处理逻辑,持有锁2秒
// 重置条件(避免重复处理)
condition = 0;
}
// 释放锁
mutex_unlock(&suspend_mutex);
// printk(KERN_INFO "Waiting thread released suspend_mutex\n");
}
return 0;
}
// 唤醒线程:主动调用wake_up唤醒等待队列
static int waking_thread(void *data)
{
while (!kthread_should_stop()) {
// 第二次唤醒:设置condition后唤醒
// printk(KERN_INFO "Waking thread: setting condition and calling wake_up()\n");
condition = 1;
wake_up(&my_waitq);
msleep(10);
}
return 0;
}
// suspend回调函数:系统休眠时调用
static int my_suspend(struct device *dev)
{
printk(KERN_INFO "Entering suspend callback: trying to acquire suspend_mutex...\n");
// 尝试获取锁,可能被等待线程持有而阻塞
mutex_lock(&suspend_mutex);
printk(KERN_INFO "Suspend callback acquired suspend_mutex...\n");
// 模拟保存设备状态
msleep(2000);
mutex_unlock(&suspend_mutex);
printk(KERN_INFO "Leaving suspend callback\n");
return 0;
// return -EBUSY;
}
// resume回调函数:系统唤醒时调用
static int my_resume(struct device *dev)
{
printk(KERN_INFO "Entering resume callback\n");
msleep(3000);
return 0;
}
// 设备电源管理操作结构体
static const struct dev_pm_ops my_pm_ops = {
.suspend = my_suspend,
.resume = my_resume,
};
// 平台驱动结构体
static struct platform_driver my_drv = {
.driver = {
.name = "wakeup_suspend_demo",
.pm = &my_pm_ops,
},
};
static struct platform_device *my_dev;
static struct task_struct *wait_task, *wake_task;
// 模块初始化
static int __init my_module_init(void)
{
init_waitqueue_head(&my_waitq);
if(1) {
// 创建等待线程
wait_task = kthread_run(waiting_thread, NULL, "waiting_thread");
if (IS_ERR(wait_task)) {
printk(KERN_ERR "Failed to create waiting thread\n");
return PTR_ERR(wait_task);
}
// 创建唤醒线程
wake_task = kthread_run(waking_thread, NULL, "waking_thread");
if (IS_ERR(wake_task)) {
printk(KERN_ERR "Failed to create waking thread\n");
kthread_stop(wait_task);
return PTR_ERR(wake_task);
}
}
// 注册平台设备和驱动
my_dev = platform_device_register_simple("wakeup_suspend_demo", -1, NULL, 0);
if (IS_ERR(my_dev)) {
printk(KERN_ERR "Failed to register platform device\n");
kthread_stop(wake_task);
kthread_stop(wait_task);
return PTR_ERR(my_dev);
}
if (platform_driver_register(&my_drv)) {
printk(KERN_ERR "Failed to register platform driver\n");
platform_device_unregister(my_dev);
kthread_stop(wake_task);
kthread_stop(wait_task);
return -1;
}
printk(KERN_INFO "Module loaded. Test with: echo mem > /sys/power/state\n");
return 0;
}
// 模块退出
static void __exit my_module_exit(void)
{
platform_driver_unregister(&my_drv);
platform_device_unregister(my_dev);
kthread_stop(wake_task);
kthread_stop(wait_task);
printk(KERN_INFO "Module unloaded\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
wait_event_interruptible 和 wait_event_freezable核心关键
图片
结论
输出结论待查资料问题
- 问题 1:?
- 问题 2:?
参考链接
- 官方文档
