休眠与唤醒机制

时间:2022-07-23
本文章向大家介绍休眠与唤醒机制,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1.休眠方式

在内核中,休眠方式有很多种,可以通过下面命令查看

# cat /sys/power/state  //来得到内核支持哪几种休眠方式. 

常用的休眠方式有 freeze,standby, mem, disk

  • freeze: 冻结I/O设备,将它们置于低功耗状态,使处理器进入空闲状态。唤醒最快,耗电比其它standby, mem,disk方式高。
  • standby: 除了冻结I/O设备外,还会暂停系统,唤醒较快,耗电比其它 mem, disk方式高
  • mem: 将运行状态数据存到内存,并关闭外设,进入等待模式,唤醒较慢,耗电比disk方式高
  • disk: 将运行状态数据存到硬盘,然后关机,唤醒最慢

示例:

 # echo standby > /sys/power/state  // 命令系统进入standby休眠.           

2.唤醒方式

当我们休眠时,如果想唤醒,则需要添加中断唤醒源,使得在休眠时,这些中断是设为开启的,当有中断来,则会退出唤醒,常见的中断源有按键,USB等。

3.底层实现

代码参考: kernel/drivers/input/keyboard/gpio_keys.c

static int __maybe_unused gpio_keys_suspend(struct device *dev)
{
……
 enable_irq_wake(irq);
……
 return 0;
}

static int __maybe_unused gpio_keys_resume(struct device *dev)
{
……
 disable_irq_wake(irq);
……
 return 0;
}

static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);

static struct platform_driver gpio_keys_device_driver = {
 .probe  = gpio_keys_probe,
 .shutdown = gpio_keys_shutdown,
 .driver  = {
  .name = "gpio-keys",
  .pm = &gpio_keys_pm_ops,
  .of_match_table = gpio_keys_of_match,
  .dev_groups = gpio_keys_groups,
 }
};

注: 上面代码中,gpio_keys_suspend,gpio_keys_resume中没有直接出现enable_irq_wake和disable_irq_wake,但是最终是会调用这俩API。

通过实例发现:休眠唤醒的设计,只需要在gpio_keys_device_driver 中实例driver成员的pm成员。SIMPLE_DEV_PM_OPS是Linux封装的一层结构体:

#ifdef CONFIG_PM_SLEEP
#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) 
 .suspend = suspend_fn, 
 .resume = resume_fn, 
 .freeze = suspend_fn, 
 .thaw = resume_fn, 
 .poweroff = suspend_fn, 
 .restore = resume_fn,
#else
#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)
#endif

#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) 
const struct dev_pm_ops name = { 
 SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) 
}

将源代码宏展开:

//static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
const struct dev_pm_ops name = { 
 .suspend = gpio_keys_suspend,
 .resume  = gpio_keys_resume,
}

3.总结

  1. 在实际应用中,需要按键实现休眠唤醒,只需要实现platform_driver->driver->pm下suspend和resume成员函数即可。然后在suspend和resume中增加按键中断唤醒使能和按键唤醒失能。
  2. 流程:在linux要执行休眠时,换遍历一遍所有注册到内核驱动的suspend函数,执行suspend内部代码;在被唤醒时会遍历resume函数,执行内部代码。
  3. 至于为什么都要执行中断唤醒失能?网上的一种说法是如果在执行enable_irq_wake(irq)之前,中断已经处于可唤醒使能,会出现报错。所以在每次唤醒前先disable_irq_wake(irq),休眠时enable_irq_wake(irq)。
  4. 对于休眠唤醒,Linux内核实现起来很复杂,但是对于驱动开发来讲,使用起来较为方便,这也是操作系统的意义所在:严格的分层思想,复杂的流程由内核实现,并提供API供开发人员使用。学习内核的具体实现对编程功力有很大帮助,后续继续分章节介绍其内核休眠唤醒机制具体的内核实现流程。

引用文章:1.Linux电源管理-休眠与唤醒

如有技术交流需要,请关注“开源519”公众号。