Android休眠与唤醒驱动流程分析

标准Linux休眠过程:

  • powermanagement notifiers are executed with PM_SUSPEND_PREPARE

  • tasksare frozen

  • targetsystem sleep state is announced to the platform-handling code

  • devicesare suspended

  • platform-specificglobal suspend preparation methods are executed

  • non-bootCPUs are taken off-line

  • interruptsare disabled on the remaining (main) CPU

  • latesuspend of devices is carried out (一般有一些BUSdriver的动作进行)‏

  • platform-specificglobal methods are invoked to put the system to sleep

标准linux唤醒过程:

  • themain CPU is switched to the appropriate mode, if necessary

  • earlyresume of devices is carried out(一般有一些BUSdriver的动作进行)‏

  • interruptsare enabled on the main CPU

  • non-bootCPUs are enabled

  • platform-specificglobal resume preparation methods are invoked

  • devicesare woken up

  • tasksare thawed

  • powermanagement notifiers are executed with PM_POST_SUSPEND

用户可以通过sys文件系统控制系统进入休眠:

查看系统支持的休眠方式:

#cat/sys/power/state

常见有standby(suspendto RAM)、mem(suspend toRAM)和disk(suspend todisk),只是standby耗电更多,返回到正常工作状态的时间更短。

通过 #echomem > /sys/power/state让系统进入休眠。

Android休眠与唤醒

android是在传统的linux内核电源管理设计的基础上,结合手机设计的实际需求而进化出的一套电源管理系统,其核心内容有:wakelock、early_suspend与late_resume。

wakelock在Android的电源管理系统中扮演一个核心的角色。wakelock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得。这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。

当系统在启动完毕后,会自己去加一把名为“main“的锁,而当系统有意愿去睡眠时则会先去释放这把“main”锁,在android中,在early_suspend的最后一步会去释放“main”锁(wake_unlock:main)。释放完后则会去检查是否还有其他存在的锁,如果没有则直接进入睡眠过程。

它的缺点是,如果有某一应用获锁而不释放或者因一直在执行某种操作而没时间来释放的话,则会导致系统一直进入不了睡眠状态,功耗过大。

early_suspend:先与linux内核的睡眠过程被调用。一般在手机系统的设计中对背光的操作等采用此类方法,因为背光需要的能耗过大。当然此操作与late_resume是配套使用的。一些在内核中要预先进行处理的事件可以先注册上early_suspend函数,当系统要进入睡眠之前会首先调用这些注册的函数。

本文中,linuxkernel版本为linux-2.6.29,android版本为android 2.1

与android休眠唤醒主要相关的文件主要有:

  • linux_source/kernel/power/main.c

  • linux_source/kernel/power/earlysuspend.c

  • linux_source/kernel/power/wakelock.c

  • linux_source/kernel/power/process.c

  • linux_source/driver/base/power/main.c

  • linux_source/arch/xxx/mach-xxx/pm.c或linux_source/arch/xxx/plat-xxx/pm.c

Android休眠过程如下:

当用户读写/sys/power/state时,linux_source/kernel/power/main.c中的state_store()函数会被调用。其中,android的early_suspend会执行request_suspend_state(state);而标准的linux休眠则执行error= enter_state(state);

static ssize_t state_store(structkobject *kobj, struct kobj_attribute *attr,

const char *buf, size_t n)

{

#ifdef CONFIG_SUSPEND

#ifdef CONFIG_EARLYSUSPEND

suspend_state_t state =PM_SUSPEND_ON;

#else

suspend_state_t state =PM_SUSPEND_STANDBY;

#endif

const char * const *s;

#endif

char *p;

int len;

int error = -EINVAL;

p = memchr(buf, '\n', n);

len = p ? p - buf : n;

/* First, check if we are requestedto hibernate */

if (len == 4 &&!strncmp(buf, "disk", len)) {

error = hibernate();

goto Exit;

}

#ifdef CONFIG_SUSPEND

for (s = &pm_states[state];state < PM_SUSPEND_MAX; s++, state++) {

if (*s && len ==strlen(*s) && !strncmp(buf, *s, len))

break;

}

if (state < PM_SUSPEND_MAX &&*s)

#ifdefCONFIG_EARLYSUSPEND

if (state == PM_SUSPEND_ON ||valid_state(state)) {

error = 0;

request_suspend_state(state);

}

#else

error= enter_state(state);§

#endif

#endif

Exit:

return error ? error : n;

}

在request_suspend_state(state)函数中,会调用early_suspend_work的工作队列,从而进入early_suspend()函数中。

staticDECLARE_WORK(early_suspend_work, early_suspend);

voidrequest_suspend_state(suspend_state_t new_state)

{

unsigned long irqflags;

int old_sleep;

spin_lock_irqsave(&state_lock,irqflags);

old_sleep = state &SUSPEND_REQUESTED;

if (debug_mask &DEBUG_USER_STATE) {

struct timespec ts;

struct rtc_time tm;

getnstimeofday(&ts);

rtc_time_to_tm(ts.tv_sec, &tm);

pr_info("request_suspend_state:%s (%d->%d) at %lld "

"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)\n",

new_state != PM_SUSPEND_ON ?"sleep" : "wakeup",

requested_suspend_state,new_state,

ktime_to_ns(ktime_get()),

tm.tm_year + 1900, tm.tm_mon + 1,tm.tm_mday,

tm.tm_hour, tm.tm_min, tm.tm_sec,ts.tv_nsec);

}

if (!old_sleep && new_state!= PM_SUSPEND_ON) {

state |= SUSPEND_REQUESTED;

queue_work(suspend_work_queue,&early_suspend_work);

} else if (old_sleep &&new_state == PM_SUSPEND_ON) {

state &= ~SUSPEND_REQUESTED;

wake_lock(&main_wake_lock);

queue_work(suspend_work_queue,&late_resume_work);§

}

requested_suspend_state =new_state;

spin_unlock_irqrestore(&state_lock,irqflags);

}

在early_suspend()函数中,首先要判断当前请求的状态是否还是suspend,若不是,则直接退出了;若是,函数会调用已经注册的early_suspend的函数。然后同步文件系统,最后释放main_wake_lock。

static void early_suspend(structwork_struct *work)

{

struct early_suspend *pos;

unsigned long irqflags;

int abort = 0;

mutex_lock(&early_suspend_lock);

spin_lock_irqsave(&state_lock,irqflags);

if (state ==SUSPEND_REQUESTED)

state |= SUSPENDED;

else

abort = 1;

spin_unlock_irqrestore(&state_lock,irqflags);

if (abort) {

if (debug_mask &DEBUG_SUSPEND)

pr_info("early_suspend:abort, state %d\n", state);

mutex_unlock(&early_suspend_lock);

goto abort;

}

if (debug_mask & DEBUG_SUSPEND)

pr_info("early_suspend: callhandlers\n");

list_for_each_entry(pos,&early_suspend_handlers, link) {

if(pos->suspend != NULL)

pos->suspend(pos);

}

mutex_unlock(&early_suspend_lock);

if (debug_mask & DEBUG_SUSPEND)

pr_info("early_suspend:sync\n");

sys_sync();

abort:

spin_lock_irqsave(&state_lock,irqflags);

if (state ==SUSPEND_REQUESTED_AND_SUSPENDED)

wake_unlock(&main_wake_lock);

spin_unlock_irqrestore(&state_lock,irqflags);

}

在wake_unlock(),删除链表中wake_lock节点,判断当前是否存在wake_lock,若wake_lock的数目为0,则调用工作队列suspend_work,进入suspend状态。

staticDECLARE_WORK(suspend_work, suspend);

void wake_unlock(struct wake_lock*lock)

{

int type;

unsigned long irqflags;

spin_lock_irqsave(&list_lock,irqflags);

type = lock->flags &WAKE_LOCK_TYPE_MASK;

#ifdef CONFIG_WAKELOCK_STAT

wake_unlock_stat_locked(lock, 0);

#endif

if (debug_mask &DEBUG_WAKE_LOCK)

pr_info("wake_unlock: %s\n",lock->name);

lock->flags &=~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);

list_del(&lock->link);

list_add(&lock->link,&inactive_locks);

if (type == WAKE_LOCK_SUSPEND) {

longhas_lock = has_wake_lock_locked(type);

if (has_lock > 0) {

if (debug_mask &DEBUG_EXPIRE)

pr_info("wake_unlock: %s,start expire timer, "

"%ld\n", lock->name,has_lock);

mod_timer(&expire_timer,jiffies + has_lock);

} else {

if (del_timer(&expire_timer))

if (debug_mask &DEBUG_EXPIRE)

pr_info("wake_unlock: %s,stop expire "

"timer\n",lock->name);

if(has_lock == 0)

queue_work(suspend_work_queue,&suspend_work);

}

if (lock == &main_wake_lock) {

if (debug_mask &DEBUG_SUSPEND)

print_active_locks(WAKE_LOCK_SUSPEND);

#ifdef CONFIG_WAKELOCK_STAT

update_sleep_wait_stats_locked(0);

#endif

}

}

spin_unlock_irqrestore(&list_lock,irqflags);

}

在suspend()函数中,先判断当前是否有wake_lock,若有,则退出;然后同步文件系统,最后调用pm_suspend()函数。

static void suspend(structwork_struct *work)

{

int ret;

int entry_event_num;

if(has_wake_lock(WAKE_LOCK_SUSPEND)) {

if (debug_mask &DEBUG_SUSPEND)

pr_info("suspend: abortsuspend\n");

return;

}

entry_event_num =current_event_num;

sys_sync();

if (debug_mask & DEBUG_SUSPEND)

pr_info("suspend: entersuspend\n");

ret =pm_suspend(requested_suspend_state);

if (debug_mask &DEBUG_EXIT_SUSPEND) {

struct timespec ts;

struct rtc_time tm;

getnstimeofday(&ts);

rtc_time_to_tm(ts.tv_sec, &tm);

pr_info("suspend: exitsuspend, ret = %d "

"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)\n", ret,

tm.tm_year + 1900, tm.tm_mon + 1,tm.tm_mday,

tm.tm_hour, tm.tm_min, tm.tm_sec,ts.tv_nsec);

}

if (current_event_num ==entry_event_num) {

if (debug_mask &DEBUG_SUSPEND)

pr_info("suspend: pm_suspendreturned with no event\n");

wake_lock_timeout(&unknown_wakeup,HZ / 2);

}

}

在pm_suspend()函数中,enter_state()函数被调用,从而进入标准linux休眠过程。

int pm_suspend(suspend_state_tstate)

{

if (state > PM_SUSPEND_ON &&state <= PM_SUSPEND_MAX)

returnenter_state(state);

return -EINVAL;

}

在enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用suspend_prepare()来冻结进程,最后调用suspend_devices_and_enter()让外设进入休眠。

static intenter_state(suspend_state_t state)

{

int error;

if (!valid_state(state))

return -ENODEV;

if (!mutex_trylock(&pm_mutex))

return -EBUSY;

printk(KERN_INFO "PM: Syncingfilesystems ... ");

sys_sync();

printk("done.\n");

pr_debug("PM: Preparing systemfor %s sleep\n", pm_states[state]);

error =suspend_prepare();

if (error)

goto Unlock;

if (suspend_test(TEST_FREEZER))

goto Finish;

pr_debug("PM: Entering %ssleep\n", pm_states[state]);

error =suspend_devices_and_enter(state);

Finish:

pr_debug("PM: Finishingwakeup.\n");

suspend_finish();

Unlock:

mutex_unlock(&pm_mutex);

return error;

}

在suspend_prepare()函数中,先通过pm_prepare_console();给suspend分配一个虚拟终端来输出信息,再广播一个系统进入suspend的通报,关闭用户态的helper进程,然后调用suspend_freeze_processes()来冻结进程,最后会尝试释放一些内存。

static int suspend_prepare(void)

{

int error;

unsigned int free_pages;

if (!suspend_ops ||!suspend_ops->enter)

return -EPERM;

pm_prepare_console();

error =pm_notifier_call_chain(PM_SUSPEND_PREPARE);

if (error)

goto Finish;

error =usermodehelper_disable();

if (error)

goto Finish;

if(suspend_freeze_processes()) {

error = -EAGAIN;

goto Thaw;

}

free_pages =global_page_state(NR_FREE_PAGES);

if (free_pages <FREE_PAGE_NUMBER) {

pr_debug("PM: free somememory\n");

shrink_all_memory(FREE_PAGE_NUMBER- free_pages);

if (nr_free_pages() <FREE_PAGE_NUMBER) {

error = -ENOMEM;

printk(KERN_ERR "PM: Noenough memory\n");

}

}

if (!error)

return 0;

Thaw:

suspend_thaw_processes();

usermodehelper_enable();

Finish:

pm_notifier_call_chain(PM_POST_SUSPEND);

pm_restore_console();

return error;

}

在suspend_freeze_processes()函数中调用了freeze_processes()函数,而freeze_processes()函数中又调用了try_to_freeze_tasks()来完成冻结任务。在冻结过程中,会判断当前进程是否有wake_lock,若有,则冻结失败,函数会放弃冻结。

static int try_to_freeze_tasks(boolsig_only)

{

struct task_struct *g, *p;

unsigned long end_time;

unsigned int todo;

struct timeval start, end;

u64 elapsed_csecs64;

unsigned int elapsed_csecs;

unsigned int wakeup = 0;

do_gettimeofday(&start);

end_time = jiffies + TIMEOUT;

do {

todo = 0;

read_lock(&tasklist_lock);

do_each_thread(g,p) {

if (frozen(p) || !freezeable(p))

continue;

if(!freeze_task(p, sig_only))

continue;

/*

* Now that we've doneset_freeze_flag, don't

* perturb a task in TASK_STOPPEDor TASK_TRACED.

* It is "frozen enough". If the task does wake

* up, it will immediately calltry_to_freeze.

*/

if (!task_is_stopped_or_traced(p)&&

!freezer_should_skip(p))

todo++;

} while_each_thread(g, p);

read_unlock(&tasklist_lock);

yield(); /* Yield is okay here*/

if (todo &&has_wake_lock(WAKE_LOCK_SUSPEND)) {

wakeup = 1;

break;

}

if (time_after(jiffies, end_time))

break;

} while (todo);

do_gettimeofday(&end);

elapsed_csecs64 =timeval_to_ns(&end) - timeval_to_ns(&start);

do_div(elapsed_csecs64,NSEC_PER_SEC / 100);

elapsed_csecs = elapsed_csecs64;

if (todo) {

/* This does not unfreezeprocesses that are already frozen

* (we have slightly ugly callingconvention in that respect,

* and caller must callthaw_processes() if something fails),

* but it cleans up leftoverPF_FREEZE requests.

*/

if(wakeup) {

printk("\n");

printk(KERN_ERR "Freezing of%s aborted\n",

sig_only ? "user space ": "tasks ");

}

else {

printk("\n");

printk(KERN_ERR "Freezing oftasks failed after %d.%02d seconds "

"(%d tasks refusing tofreeze):\n",

elapsed_csecs / 100,elapsed_csecs % 100, todo);

show_state();

}

read_lock(&tasklist_lock);

do_each_thread(g, p) {

task_lock(p);

if (freezing(p) &&!freezer_should_skip(p))

printk(KERN_ERR " %s\n",p->comm);

cancel_freezing(p);

task_unlock(p);

} while_each_thread(g, p);

read_unlock(&tasklist_lock);

} else {

printk("(elapsed %d.%02dseconds) ", elapsed_csecs / 100,

elapsed_csecs % 100);

}

return todo ? -EBUSY : 0;

}

到现在,所有的进程(也包括workqueue/kthread)都已经停止了,内核态进程有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁,所以在外设suspend()函数里面作lock/unlock锁要非常小心,建议不要在外设的suspend()里面等待锁。而且suspend的过程中,有一些log是无法输出的,所以一旦出现问题,非常难调试。

回到enter_state()函数中,再冻结进程完成后,调用suspend_devices_and_enter()函数让外设进入休眠。该函数中,首先休眠串口(之后不能再显示log,解决方法为在kernel配置选项的cmd_line中,添加”no_console_suspend”选项),再通过device_suspend()函数调用各驱动的suspend函数。

当外设进入休眠后,suspend_ops->prepare()被调用,suspend_ops是板级的PM操作(本文中粉红色的函数,依赖于具体的平台),以s3c6410为例,其注册在linux_source/arch/arm/plat-s3c64xx/pm.c中,只定义了suspend_ops->enter()函数。

static struct platform_suspend_opss3c6410_pm_ops = {

.enter = s3c6410_pm_enter,

.valid = suspend_valid_only_mem,

};

接下来,多CPU中的非启动CPU被关闭。

intsuspend_devices_and_enter(suspend_state_t state)

{

int error;

if (!suspend_ops)

return -ENOSYS;

if (suspend_ops->begin) {

error =suspend_ops->begin(state);

if (error)

goto Close;

}

suspend_console();

suspend_test_start();

error =device_suspend(PMSG_SUSPEND);

if (error) {

printk(KERN_ERR "PM: Somedevices failed to suspend\n");

goto Recover_platform;

}

suspend_test_finish("suspenddevices");

if (suspend_test(TEST_DEVICES))

goto Recover_platform;

if (suspend_ops->prepare) {

error =suspend_ops->prepare();

if (error)

goto Resume_devices;

}

if (suspend_test(TEST_PLATFORM))

goto Finish;

error =disable_nonboot_cpus();

if (!error &&!suspend_test(TEST_CPUS))

suspend_enter(state);

enable_nonboot_cpus();

Finish:

if (suspend_ops->finish)

suspend_ops->finish();

Resume_devices:

suspend_test_start();

device_resume(PMSG_RESUME);

suspend_test_finish("resumedevices");

resume_console();

Close:

if (suspend_ops->end)

suspend_ops->end();

return error;

Recover_platform:

if (suspend_ops->recover)

suspend_ops->recover();

goto Resume_devices;

}

接下来suspend_enter()被调用,该函数首先关闭IRQ,然后调用device_power_down(),它会调用suspend_late()函数,这个函数是系统真正进入休眠最后调用的函数,通常会在这个函数中作最后的检查,接下来休眠所有的系统设备和总线。最后调用suspend_pos->enter()来使CPU进入省电状态。这时候,整个休眠过程完成,代码的执行也就停在这里了。

static intsuspend_enter(suspend_state_t state)

{

int error = 0;

device_pm_lock();

#ifdef CONFIG_CPU_FREQ

cpufreq_get_cpufreq_name(0);

strcpy(governor_name,cpufreq_governor_name);

if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {

cpufreq_set_policy(0,"performance");

}

#endif /* CONFIG_CPU_FREQ */

arch_suspend_disable_irqs();

BUG_ON(!irqs_disabled());

if ((error =device_power_down(PMSG_SUSPEND))){

printk(KERN_ERR "PM: Somedevices failed to power down\n");

goto Done;

}

error =sysdev_suspend(PMSG_SUSPEND);

if (!error) {

if (!suspend_test(TEST_CORE))

error =suspend_ops->enter(state);

sysdev_resume();

}

device_power_up(PMSG_RESUME);

Done:

arch_suspend_enable_irqs();

#ifdef CONFIG_CPU_FREQ

if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {

cpufreq_set_policy(0,governor_name);

}

#endif /* CONFIG_CPU_FREQ */

BUG_ON(irqs_disabled());

device_pm_unlock();

return error;

}

在suspend_pos->enter()所对应的函数中,代码最终停止在pm_cpu_sleep();处。

static ints3c6410_pm_enter(suspend_state_t state)

{

……

s3c6410_pm_do_save(gpio_save,ARRAY_SIZE(gpio_save));

s3c6410_pm_do_save(irq_save,ARRAY_SIZE(irq_save));

s3c6410_pm_do_save(core_save,ARRAY_SIZE(core_save));

s3c6410_pm_do_save(sromc_save,ARRAY_SIZE(sromc_save));

/* Clear WAKEUP_STAT register fornext wakeup -jc.lee */

/* If this register do not becleared, Wakeup will be failed */

__raw_writel(__raw_readl(S3C_WAKEUP_STAT),S3C_WAKEUP_STAT);

#ifdef CONFIG_MACH_SMDK6410

/* ALL sub block "ON"before enterring sleep mode - EVT0 bug*/

__raw_writel(0xffffff00,S3C_NORMAL_CFG);

/* Open all clock gate to entersleep mode - EVT0 bug*/

__raw_writel(0xffffffff,S3C_HCLK_GATE);

__raw_writel(0xffffffff,S3C_PCLK_GATE);

__raw_writel(0xffffffff,S3C_SCLK_GATE);

……

/* s3c6410_cpu_save will also actas our return point from when

* we resume as it saves its ownregister state, so use the return

* code to differentiate returnfrom save and return from sleep */

if(s3c6410_cpu_save(regs_save) == 0) {

flush_cache_all();

pm_cpu_sleep();

}

/* restorethe cpu state */

cpu_init();

__raw_writel(s3c_eint_mask_val,S3C_EINT_MASK);

/* restore the system state */

s3c6410_pm_do_restore_core(core_save,ARRAY_SIZE(core_save));

s3c6410_pm_do_restore(sromc_save,ARRAY_SIZE(sromc_save));

……

}

Android唤醒过程如下:

如果在休眠中系统被中断或者其他事件唤醒,接下来的代码就从suspend完成的地方开始执行,以s3c6410为例,即pm.c中的s3c6410_pm_enter()中的cpu_init(),然后执行suspend_enter()的sysdev_resume()函数,唤醒系统设备和总线,使能系统中断。

static intsuspend_enter(suspend_state_t state)

{

int error = 0;

device_pm_lock();

#ifdef CONFIG_CPU_FREQ

cpufreq_get_cpufreq_name(0);

strcpy(governor_name,cpufreq_governor_name);

if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {

cpufreq_set_policy(0,"performance");

}

#endif /* CONFIG_CPU_FREQ */

arch_suspend_disable_irqs();

BUG_ON(!irqs_disabled());

if ((error =device_power_down(PMSG_SUSPEND))) {

printk(KERN_ERR "PM: Somedevices failed to power down\n");

goto Done;

}

error =sysdev_suspend(PMSG_SUSPEND);

if (!error) {

if (!suspend_test(TEST_CORE))

error =suspend_ops->enter(state); //suspend过程完成处

sysdev_resume();

}

device_power_up(PMSG_RESUME);

Done:

arch_suspend_enable_irqs();

#ifdef CONFIG_CPU_FREQ

if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {

cpufreq_set_policy(0,governor_name);

}

#endif /* CONFIG_CPU_FREQ */

BUG_ON(irqs_disabled());

device_pm_unlock();

return error;

}

然后回到suspend_devices_and_enter()函数中,使能休眠时候停止掉的非启动CPU,继续唤醒每个设备,使能终端。

intsuspend_devices_and_enter(suspend_state_t state)

{

int error;

if (!suspend_ops)

return -ENOSYS;

if (suspend_ops->begin) {

error =suspend_ops->begin(state);

if (error)

goto Close;

}

suspend_console();

suspend_test_start();

error =device_suspend(PMSG_SUSPEND);

if (error) {

printk(KERN_ERR "PM: Somedevices failed to suspend\n");

goto Recover_platform;

}

suspend_test_finish("suspenddevices");

if (suspend_test(TEST_DEVICES))

goto Recover_platform;

if (suspend_ops->prepare) {

error =suspend_ops->prepare();

if (error)

goto Resume_devices;

}

if (suspend_test(TEST_PLATFORM))

goto Finish;

error = disable_nonboot_cpus();

if (!error &&!suspend_test(TEST_CPUS))

suspend_enter(state); //suspend过程完成处

enable_nonboot_cpus();

Finish:

if (suspend_ops->finish)

suspend_ops->finish();

Resume_devices:

suspend_test_start();

device_resume(PMSG_RESUME);

suspend_test_finish("resumedevices");

resume_console();

Close:

if (suspend_ops->end)

suspend_ops->end();

return error;

Recover_platform:

if (suspend_ops->recover)

suspend_ops->recover();

goto Resume_devices;

}

当suspend_devices_and_enter()执行完成后,系统外设已经唤醒,但进程依然是冻结的状态,返回到enter_state函数中,调用suspend_finish()函数。

static intenter_state(suspend_state_t state)

{

int error;

if (!valid_state(state))

return -ENODEV;

if (!mutex_trylock(&pm_mutex))

return -EBUSY;

printk(KERN_INFO "PM: Syncingfilesystems ... ");

sys_sync();

printk("done.\n");

pr_debug("PM: Preparing systemfor %s sleep\n", pm_states[state]);

error = suspend_prepare();

if (error)

goto Unlock;

if (suspend_test(TEST_FREEZER))

goto Finish;

pr_debug("PM: Entering %ssleep\n", pm_states[state]);

error =suspend_devices_and_enter(state); //suspend过程完成处

Finish:

pr_debug("PM: Finishingwakeup.\n");

suspend_finish();

Unlock:

mutex_unlock(&pm_mutex);

return error;

}

在suspend_finish()函数中,解冻进程和任务,使能用户空间helper进程,广播一个系统从suspend状态退出的notify,唤醒终端。

static void suspend_finish(void)

{

suspend_thaw_processes();

usermodehelper_enable();

pm_notifier_call_chain(PM_POST_SUSPEND);

pm_restore_console();

}

当所有的唤醒已经结束以后,用户进程都已经开始运行了,但没点亮屏幕,唤醒通常会是以下的几种原因:

如果是来电,那么Modem会通过发送命令给rild来让rild通知WindowManager有来电响应,这样就会远程调用PowerManagerService来写”on”到/sys/power/state来调用lateresume(),执行点亮屏幕等操作。

用户按键事件会送到WindowManager中,WindowManager会处理这些按键事件,按键分为几种情况,如果按键不是唤醒键,那么WindowManager会主动放弃wakeLock来使系统进入再次休眠;如果按键是唤醒键,那么WindowManger就会调用PowerManagerService中的接口来执行lateResume。

当”on”被写入到/sys/power/state之后,同early_suspend过程,request_suspend_state()被调用,只是执行的工作队列变为late_resume_work。在late_resume函数中,唤醒调用了early_suspend的设备。

staticDECLARE_WORK(late_resume_work, late_resume);

static void late_resume(structwork_struct *work)

{

struct early_suspend *pos;

unsigned long irqflags;

int abort = 0;

mutex_lock(&early_suspend_lock);

spin_lock_irqsave(&state_lock,irqflags);

if (state == SUSPENDED)

state &= ~SUSPENDED;

else

abort = 1;

spin_unlock_irqrestore(&state_lock,irqflags);

if (abort) {

if (debug_mask &DEBUG_SUSPEND)

pr_info("late_resume: abort,state %d\n", state);

goto abort;

}

if (debug_mask & DEBUG_SUSPEND)

pr_info("late_resume: callhandlers\n");

list_for_each_entry_reverse(pos,&early_suspend_handlers, link)

if(pos->resume != NULL)

pos->resume(pos);

if (debug_mask & DEBUG_SUSPEND)

pr_info("late_resume:done\n");

abort:

mutex_unlock(&early_suspend_lock);

}

关于wake_lock

在上文中,已经介绍了wakelock机制,下面从代码的角度进行介绍。

wakelock有3种类型,常用为WAKE_LOCK_SUSPEND,作用是防止系统进入睡眠。其他类型不是很清楚。

enum {

WAKE_LOCK_SUSPEND, /* Preventsuspend */

WAKE_LOCK_IDLE, /* Prevent lowpower idle */

WAKE_LOCK_TYPE_COUNT

};

Wakelock有加锁和解锁2种操作,加锁有2种方式,第一种是永久加锁(wake_lock),这种锁必须手动的解锁;另一种是超时锁(wake_lock_timeout),这种锁在过去指定时间后,会自动解锁。

void wake_lock(struct wake_lock*lock)

{

wake_lock_internal(lock, 0, 0);

}

void wake_lock_timeout(structwake_lock *lock, long timeout)

{

wake_lock_internal(lock, timeout,1);

}

对于wakelock,timeout= has_timeout = 0;直接加锁后,然后退出;

static void wake_lock_internal(

struct wake_lock *lock, longtimeout, int has_timeout)

{

int type;

unsigned long irqflags;

long expire_in;

spin_lock_irqsave(&list_lock,irqflags);

type = lock->flags &WAKE_LOCK_TYPE_MASK;

BUG_ON(type >=WAKE_LOCK_TYPE_COUNT);

BUG_ON(!(lock->flags &WAKE_LOCK_INITIALIZED));

#ifdef CONFIG_WAKELOCK_STAT

if (type == WAKE_LOCK_SUSPEND &&wait_for_wakeup) {

if (debug_mask & DEBUG_WAKEUP)

pr_info("wakeup wake lock:%s\n", lock->name);

wait_for_wakeup = 0;

lock->stat.wakeup_count++;

}

if ((lock->flags &WAKE_LOCK_AUTO_EXPIRE) &&

(long)(lock->expires -jiffies) <= 0) {

wake_unlock_stat_locked(lock, 0);

lock->stat.last_time =ktime_get();

}

#endif

if (!(lock->flags &WAKE_LOCK_ACTIVE)) {

lock->flags |=WAKE_LOCK_ACTIVE;

#ifdef CONFIG_WAKELOCK_STAT

lock->stat.last_time =ktime_get();

#endif

}

list_del(&lock->link);

if(has_timeout) {

if (debug_mask &DEBUG_WAKE_LOCK)

pr_info("wake_lock: %s, type%d, timeout %ld.%03lu\n",

lock->name, type, timeout /HZ,

(timeout % HZ) * MSEC_PER_SEC /HZ);

lock->expires= jiffies + timeout;

lock->flags |=WAKE_LOCK_AUTO_EXPIRE;

list_add_tail(&lock->link,&active_wake_locks[type]);

} else {

if (debug_mask &DEBUG_WAKE_LOCK)

pr_info("wake_lock: %s, type%d\n", lock->name, type);

lock->expires = LONG_MAX;

lock->flags &=~WAKE_LOCK_AUTO_EXPIRE;

list_add(&lock->link,&active_wake_locks[type]);

}

if (type == WAKE_LOCK_SUSPEND) {

current_event_num++;

#ifdef CONFIG_WAKELOCK_STAT

if (lock == &main_wake_lock)

update_sleep_wait_stats_locked(1);

else if(!wake_lock_active(&main_wake_lock))

update_sleep_wait_stats_locked(0);

#endif

if (has_timeout)

expire_in =has_wake_lock_locked(type);

else

expire_in = -1;

if (expire_in > 0) {

if (debug_mask &DEBUG_EXPIRE)

pr_info("wake_lock: %s,start expire timer, "

"%ld\n", lock->name,expire_in);

mod_timer(&expire_timer,jiffies + expire_in);

} else {

if (del_timer(&expire_timer))

if (debug_mask &DEBUG_EXPIRE)

pr_info("wake_lock: %s,stop expire timer\n",

lock->name);

if (expire_in == 0)

queue_work(suspend_work_queue,&suspend_work);

}

}

spin_unlock_irqrestore(&list_lock,irqflags);

}

而对于wake_lock_timeout,在经过timeout时间后,才加锁。再判断当前持有wakelock时,启动另一个定时器,在expire_timer的回调函数中再次判断是否持有wakelock。

static voidexpire_wake_locks(unsigned long data)

{

long has_lock;

unsigned long irqflags;

if (debug_mask & DEBUG_EXPIRE)

pr_info("expire_wake_locks:start\n");

spin_lock_irqsave(&list_lock,irqflags);

if (debug_mask & DEBUG_SUSPEND)

print_active_locks(WAKE_LOCK_SUSPEND);

has_lock =has_wake_lock_locked(WAKE_LOCK_SUSPEND);

if (debug_mask & DEBUG_EXPIRE)

pr_info("expire_wake_locks:done, has_lock %ld\n", has_lock);

if (has_lock== 0)

queue_work(suspend_work_queue,&suspend_work);

spin_unlock_irqrestore(&list_lock,irqflags);

}

static DEFINE_TIMER(expire_timer,expire_wake_locks, 0, 0);

在wakelock中,有2个地方可以让系统从early_suspend进入suspend状态。分别是:

  • wake_unlock中,解锁之后,若没有其他的wakelock,则进入suspend。

  • 在超时锁的定时器超时后,定时器的回调函数,会判断有没有其他的wakelock,若没有,则进入suspend。

android 系统的休眠与唤醒+linux 系统休眠的更多相关文章

  1. windows系统c盘占满/linux系统磁盘block、inode占满处理

    windows系统 下载c盘清理.bat到服务器,双击bat文件将自动清理 linux系统 先远程ssh登录上服务器,登录教程:http://www.west263.com/faq/list.asp? ...

  2. ubuntu系统备份与恢复(也适用于其他linux系统)

    在windows环境下面,有很多的备份恢复软件,系统也自带了备份恢复功能,比较知名的软件比如ghost等,为什么要备份系统,我之前一直都是系统出现问题就全新安装原版的windows,然后各种设置,各种 ...

  3. window 系统上传文件到linux 系统出现dos 格式换行符

    Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行,所以为了避免这种情况的发生,我们可以 ...

  4. 苹果IOS系统SVN命令 同样适用于linux系统

    1.将文件checkout到本地目录svn checkout path(path是服务器上的目录)例如:svn checkout svn://192.168.1.1/pro/domain简写:svn ...

  5. IT技术学习指导之Linux系统入门的4个阶段(纯干货带图)

    IT技术学习指导之Linux系统入门的4个阶段(纯干货带图) 全世界60%的人都在使用Linux.几乎没有人没有受到Linux系统的"恩惠",我们享受的大量服务(包括网页服务.聊天 ...

  6. 为什么高手离不了Linux系统?这就是我的理由

    摘要: 通过本文来记录下我在Linux系统的学习经历,聊聊我为什么离不了Linuxx系统,同时也为那些想要尝试Linux而又有所顾忌的用户答疑解惑,下面将为你介绍我所喜欢的Linux系统,这里有一些你 ...

  7. Linux基础教程(一)——Linux系统简介

    Linux的概述 Linux是基于Unix的开源免费的操作系统,由于系统的稳定性和安全性几乎成为程序代码运行的最佳系统环境.Linux是由Linus Torvalds(林纳斯·托瓦兹)起初开发的,由于 ...

  8. 极客时间-左耳听风-程序员攻略-Linux系统、内存和网络

    程序员练级攻略:Linux系统.内存和网络 Linux 系统相关 Red Hat Enterprise Linux 文档 . Linux Insides ,GitHub 上的一个开源电子书,其中讲述了 ...

  9. Linux系统起源及主流发行版

    Linux系统起源及主流发行版   本文首先介绍了三大服务器系统,然后介绍了Linux系统的出现背景.以及主要release版本,最后介绍了Linux的文件系统和目录结构.   服务器系统,即安装在服 ...

随机推荐

  1. 有道云笔记web版本居然不支持火狐

    好尴尬的火狐呀....

  2. 第200天:js---常用string原型扩展

    一.常用string原型扩展 1.在字符串末尾追加字符串 /** 在字符串末尾追加字符串 **/ String.prototype.append = function (str) { return t ...

  3. p12转pem公钥私钥

    cer格式证书生成p12文件,前面写了有一篇了. 这里是从p12文件导出公钥和私钥 //1.生成1.key文件 openssl pkcs12 -in apple_payment.p12 -nocert ...

  4. BZOJ3714 PA2014Kuglarz(最小生成树)

    每次询问所获得的可以看做是两个前缀和的异或.我们只要知道任意前缀和的异或就可以得到答案了.并且显然地,如果知道了a和b的异或及a和c的异或,也就知道了b和c的异或.所以一次询问可以看做是在两点间连边, ...

  5. Qt消息机制和事件

    Qt消息机制和事件 1 事件 事件(event)是由系统或者 Qt 本身在不同的时刻发出的.当用户按下鼠标.敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件.一些事件在对用户操作做出响应 ...

  6. 【比赛】HNOI2018 总结

    一将功成万骨枯,我就是给那些队爷做基数的 一.比赛过程 Day1 看完题,暴力好打,然后就打 觉得第三题模型很好建啊,先看第三题吧(结果第三题是最..的) 图建出来,先看树的情况,设dp试一下 结果一 ...

  7. POJ.2528 Mayor's posters (线段树 区间更新 区间查询 离散化)

    POJ.2528 Mayor's posters (线段树 区间更新 区间查询 离散化) 题意分析 贴海报,新的海报能覆盖在旧的海报上面,最后贴完了,求问能看见几张海报. 最多有10000张海报,海报 ...

  8. 【神仙题】【CF28D】 Don't fear, DravDe is kind

    传送门 Description 一个有N辆卡车的车队从城市Z驶向城市3,来到了一条叫做"恐惧隧道"的隧道.在卡车司机中,有传言说怪物DravDe在那条隧道里搜寻司机.有些司机害怕先 ...

  9. python学习(十一)测试和调试

    最近学习了python的错误处理和几种测试方法 1 try except 可以通过try except方式捕捉异常 try: print('try...') r = 10/0 print('resul ...

  10. Nginx--try_files

    Nginx的配置语法灵活,可控制度非常高.在0.7以后的版本中加入了一个try_files指令,配合命名location,可以部分替代原本常用的rewrite配置方式,提高解析效率.   作用域:se ...