suspend流程借用其他图和链接,比较详细了http://www.wowotech.net/linux_kenrel/suspend_and_resume.html
我这边记录下wakelock的相关接口和suspend过程中的frozenthread实现原理,还有部分idle流程。
wakelock内核和应用使用的相关接口 static inline void wake_lock(struct wake_lock *lock) { __pm_stay_awake(&lock->ws); } static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) { __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout)); } static inline void wake_unlock(struct wake_lock *lock) { __pm_relax(&lock->ws); } wakelock通过一个32位值来表示,高16位表示总的wakeevent发生次数,低16位标志当前置位的wakelock数量 没有wakelock时候触发suspend流程 wakeup.c可以看wakelock当前状态,是debug接口 在kernel\power\main.c封装着给应用层使用的接口,应用通过写文件来设置wakelock,/sys/power/wake_lock|wake_unlock #ifdef CONFIG_PM_WAKELOCKS static ssize_t wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return pm_show_wakelocks(buf, true); } static ssize_t wake_lock_store(struct kobject *kobj,struct kobj_attribute *attr,const char *buf, size_t n) { int error = pm_wake_lock(buf); return error ? error : n; } power_attr(wake_lock); static ssize_t wake_unlock_show(struct kobject *kobj,struct kobj_attribute *attr,char *buf) { return pm_show_wakelocks(buf, false); } static ssize_t wake_unlock_store(struct kobject *kobj,struct kobj_attribute *attr,const char *buf, size_t n) { int error = pm_wake_unlock(buf); return error ? error : n; } power_attr(wake_unlock); 在suspend流程中的suspend_prepare->suspend_freeze_processes会冻结线程 freeze_processes freeze_kernel_threads ... pm_freezing = true; ... freeze_task 对于用户线程: if (!(p->flags & PF_KTHREAD)) { fake_signal_wake_up(p); 发送一个空的信号给各个应用线程 信号处理中do_signal,调用接口设置当前线程为unint,然后切换出去 try_to_freeze_nowarn __refrigerator set_current_state(TASK_UNINTERRUPTIBLE); schedule(); 对于内核线程,要宽松的多,主要操作只是唤醒int状态的线程,然后由线程自己去处理 freeze_task wake_up_state(p, TASK_INTERRUPTIBLE); 线程创建的时候需要如下: while(kthread_freezable_should_stop) { ... } kthread_freezable_should_stop __refrigerator set_current_state(TASK_UNINTERRUPTIBLE); current->flags |= PF_FROZEN; 这样线程就不会参与调度了,切出去后不会再加入就绪队列,等到系统resume的时候再反过来依次恢复线程状态
cpu_suspend(0, XX_finish_suspend) cpu_suspend(0, XX_finish_suspend) cpu_suspend(0, XX_finish_suspend)
关于降低功耗,除了suspend,还可以让系统进入idle,毕竟suspend属于深度睡眠,进入和退出的成本都比较高。
当线程处于无事可做的时候,系统会调度到idle线程,idle线程中可以进入arm的wfi模式,具体的进入的低功耗模式还跟实际的体系架构有关,可以实现多级,每级有他对应的功耗级别和退出延迟值,系统根据运行情况选择进入哪种模式。
cpu_idle tick_nohz_idle_enter ... cpuidle_idle_call next_state = cpuidle_curr_governor->select(drv, dev) cpuidle_enter_state(dev, drv, next_state) //选择调用定制的低功耗模式,如果没有的话会进入默认的wfi模式 ... tick_nohz_idle_exit 接口类型如下: struct cpuidle_state { char name[CPUIDLE_NAME_LEN]; char desc[CPUIDLE_DESC_LEN]; unsigned int flags; unsigned int exit_latency; /* in US */ int power_usage; /* in mW */ unsigned int target_residency; /* in US */ unsigned int disable; int (*enter) (struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); int (*enter_dead) (struct cpuidle_device *dev, int index); };