linux的休眠与唤醒调试

360影视 日韩动漫 2025-09-04 11:57 1

摘要:#include #include #include #include #include #includ

第1步:打开相关内核配置

CONFIG_PM_DEBUG=yCONFIG_PM_ADVANCED_DEBUG=yCONFIG_PM_TEST_SUSPEND=yCONFIG_PM_SLEEP_DEBUG=y

第2步:使能相关设置

echo N > /sys/module/printk/parameters/console_suspendecho 1 > /sys/power/pm_print_timesecho 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/printk4 4 1 7这四个数字分别代表以下含义:第一个数字(控制台日志级别):控制哪些级别的内核消息会被打印到控制台。只有消息级别小于该值时,才会显示在控制台。例如:值为 4 时,只会显示级别 0-3 的消息(紧急、警报、严重错误、错误)。第二个数字(默认消息级别):当内核消息未明确指定级别时,使用的默认级别。例如:值为 4 时,未指定级别的消息会被视为级别 4(警告)。第三个数字(最低控制台日志级别):用于设置控制台日志级别的下限(最小值)。用户不能将第一个数字设置得比该值更小(更严格)。例如:值为 1 时,控制台日志级别不能设置为 0。第四个数字(默认控制台日志级别):内核启动时的默认控制台日志级别,也作为恢复默认设置时的参考值。可以查看休眠唤醒的时间查看休眠唤醒的结果休眠唤醒单元测试

一个实际案例:在实际休眠唤醒时,使用了wait_event_interruptible函数,然后在休眠过程中,来了信号导致线程被唤醒,操作了重要的资源,导致休眠失败。然后用wait_event_freezable函数替代解决问题

#include #include #include #include #include #include #include #include // 等待队列和条件变量static wait_queue_head_t my_waitq;static int condition = 0;// 关键资源锁(休眠流程需要访问)static DEFINE_MUTEX(suspend_mutex);// 等待线程:使用wait_event_interruptiblestatic 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");elseprintk(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");elseprintk(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核心关键

图片✅ 结论

来源:Amy一点号

相关推荐