android 内核power电源管理

2019-07-13 04:48发布

linux内核有一套电源管理的机制,休眠/唤醒在嵌入式Linux中是非常重要的部分,而android是基于linux内核的,它在此机制基本上作了改进。 本文只是针对参考代码总结的具体性的知识,其它有关系统知识网上一大堆,可自己搜索学习。   关于这部分我曾经写了份PPT作为内部技术交流稿,可下载参考下:http://download.csdn.net/detail/yunjinwang/4698512   一,设备文件的生成:/sys/power/state 内核中的相关文件: Kernel/power/main.c Kernel/power/suspend.c
  注册pm的流程: static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,   const char *buf, size_t n); static struct attribute * g[] = {&state_attr.attr, …… static struct attribute_group attr_group = {.attrs = g,}; static int __init pm_init(void); core_initcall(pm_init);   注册后生成/sys /power/state 文件,用户可以通过读写sys文件/sys /power/state 是实现控制系统休眠唤酲 echo on/mem > /sys/power/state   enter_state 是负责处理进入何种状态的函数。在linux里,电源管理基本都经过如下几步: 1. suspend_prepare():分配console终端、广播notify、冻结所有进程、保存当前状态 2. suspend_enter():休眠所有外设、关闭irq、休眠系统设备和总线、cpu省电状态 3. suspend_finish():恢复所有的进程   休眠的三个主要步骤: a,冻结用户态进程和内核态任务 b,调用注册设备的suspend回调函数 c,停止内核态所有进程,保存上下文,使CPU进入休眠。     唤醒条件:中断或事件唤醒系统 唤醒顺序: 系统设备与总线最先唤醒 恢复内核态进程 重启各个设备 Suspend_finish()解冻进程和任务并Notify状态唤醒终端   二,suspend_prepare和suspend_devices_and_enter详解 1,suspend_prepare详解 suspend_prepare - Do prep work before entering low-power state.that is called for that we're entering.Run suspend notifiers, allocate a console and stop all processes. static int suspend_prepare(void) { pm_prepare_console();   // 创建虚拟console以便导出输出打印信息,防止阻塞 error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); error = usermodehelper_disable(); error = suspend_freeze_processes(); suspend_thaw_processes();  //前面出现错误,下面是恢复操作 usermodehelper_enable();  Finish: pm_notifier_call_chain(PM_POST_SUSPEND); pm_restore_console(); }     pm_notifier_call_chain(PM_SUSPEND_PREPARE); 调用Linux系统的notifier_call_chain,通知相关的子系统正在进行suspend 系统的准备工作,提示相关子系统做出相应的操作准备。 Notifier chain是Linux的一种内核态子系统通知机制,当某一个子系统发生一些事件,会通知相关的子系统,实现信息共享,前提是其它子系统事先注册了对应的通知回调函数。 Notification chain由notifier block组成,其结构类型如下(include/linux/notifier.h): struct notifier_block{ int (*notifier_call)(struct notifier_block *self, unsigned long, void *); struct notifier_block *next; int priority;}; blocking_notifier_chain_register(struct notifier_block*); //注册函数 blocking_notifier_call_chain();   //处理函数 实例参考:/arch/arm/mach-s5pv210/cpu-freq.c http://blog.chinaunix.net/link.php?url=http://www.linuxdiyf.com%2Fbbs%2Fviewthread.php%3Ftid%3D76138
  / * usermodehelper_disable - prevent new helpers from being started usermodehelper_disable(); 一般都是用户态直接通知同核态做相应处理,但有的时候需要从内核通知用户态来处理事件,这时候就用到user mode helper  参考资料:http://news.dayoo.com/tech/201005/21/10000612_102055495.htm     /**tell processes to enter the refrigerator suspend_freeze_processes(); 冻结所有进程,并保存当前进程状态。 linux是用它的freeze框架加上信号处理来实现进程冻结的,在freeze所有进程的时候并没有将之挂起或者说使其不再运行,而是在信号处理的开始挂了一个类似于钩子的东西,把挂起进程交给信号处理来做,freeze框架要做的就是设置一些标志位来指示信号处理要冻结它了,然后设置此进程的信号附着位,这样该进程在返回用户空间的时候就会进入到冻结状态了。 /kernel/power/process.c   freeae_process(); /arch/arm/kernel/signal.c   do_signal(); 参考资料:http://blog.csdn.net/dog250/article/details/5303442   suspend_thaw_processes(); usermodehelper_enable();  Finish: pm_notifier_call_chain(PM_POST_SUSPEND); pm_restore_console(); 以上的处理是在前面的处理过程失败后,恢复到此前状态的操作     2,suspend_devices_and_enter详解 /*suspend_devices_and_enter - suspend devices and enter the desired system int suspend_devices_and_enter(suspend_state_t state) { suspend_console();   //真正挂起console dpm_suspend_start(PMSG_SUSPEND); suspend_enter(state);  Resume_devices: //当挂起出现错误或恢复系统时运行下面部分 suspend_test_start(); dpm_resume_end(PMSG_RESUME); resume_console();  } //****Prepare devices for PM transition and suspend them. dpm_suspend_start(PMSG_SUSPEND) { error = dpm_prepare(state);// Prepare all non-sysdev devices for a system PM transition. if (!error) error = dpm_suspend(state);// Execute "suspend" callbacks for all non-sysdev devices. } //**enter the desired system sleep state. should be called after devices have been suspended. static int suspend_enter(suspend_state_t state) { dpm_suspend_noirq(PMSG_SUSPEND); //Execute "late suspend" callbacks for non-sysdev devices error = disable_nonboot_cpus();//关闭非启动的CPU arch_suspend_disable_irqs(); //关闭所有的中断 error = sysdev_suspend(PMSG_SUSPEND);//关闭所有的设备 if (!error) { sysdev_resume(); } arch_suspend_enable_irqs(); BUG_ON(irqs_disabled());  Enable_cpus: enable_nonboot_cpus();  Power_up_devices: dpm_resume_noirq(PMSG_RESUME);  
三,被管理的设备注册相应的suspend和resume函数: 各设备注册:      void register_early_suspend(struct early_suspend *handler); enum { EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50, EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100, EARLY_SUSPEND_LEVEL_DISABLE_FB = 150, }; 设备休眠优先级,低--à高 struct early_suspend { struct list_head link; int level; void (*suspend)(struct early_suspend *h); void (*resume)(struct early_suspend *h); };   四,power on/off流程: 在第一步的时候,注册模块到内核中会生成一个设备文件/sys /power/state 用户可以通过读写sys文件/sys /power/state 是实现控制系统休眠唤酲 echo on/mem > /sys/power/state void request_suspend_state(suspend_state_t new_state) {……. If (new_state == PM_SUSPEND_MEM) queue_work(suspend_work_queue, &early_suspend_work); else If (new_state == PM_SUSPEND_ON) queue_work(suspend_work_queue, &late_resume_work); } 根据不同的命令queue_work相应的队列,每个队列都是各模块在启动时注册组成的, list_for_each_entry(pos, &early_suspend_handlers, link)  {
if (pos->suspend != NULL)
pos->suspend(pos);
}
list_for_each_entry_reverse(pos, &early_suspend_handlers, link) { if (pos->resume != NULL) pos->resume(pos); }   Linux 系统休眠几种状态 •echo  ???  > /dev/power/state •1,on ------ 唤醒系统resume •2,standy ---- 休眠(进程状态保存到memory,相对mem耗电,但恢复更快) •3,mem ----- 休眠(进程状态保存到memory) •4,disk ----- 休眠 (进程状态保存到磁盘,一般为swap分区)     在SMDKC11平台下,内核在调试的时候,如果进入到内核层的休眠,怎么会同时关闭console,导致无法打印,给调试带来了难度,可修改代码以达到在内核层休眠时,console仍然能够打印信息,具体方法是:在/drivers/serial/samsung.c中,将s3c24xx_serial_init()中的suspend 和resume赋值语句注销掉。