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. angularjs 指令间相互调用

    <div ng-app="app"> <div ng-controller="myctl"> <button superman s ...

  2. HDU4786_Fibonacci Tree

    题目很新颖的,略带智商,很好. 题目的意思是给你一些白色边和黑色边,现在问你能否用两色边构造出一颗生成树,且树中白色边的数量为一个Fibonacci数. 其实在没做题目之前我就已经听说了这个题目的解题 ...

  3. SDOI2017 解题报告

    数字表格 \(T\)次询问,每次给出\(n,m(n,m\le 10^6)\),\(f\)为斐波那契数列,\(f_0=0,f_1=1\),求: \[ \prod _{i=1}^n\prod _{j=1} ...

  4. luogu 1344 追查坏牛奶(最小割)

    第一问求最小割. 第二问求割边最小的最小割. 我们直接求出第二问就可以求出第一问了. 对于求割边最小,如果我们可以把每条边都附加一个1的权值,那么求最小割是不是会优先选择1最少的边呢. 但是如果直接把 ...

  5. android面试(1)----布局

    1.说出android 五中布局,并说出各自作用? FrameLayout: 堆叠布局,也是就可以堆在一起.最长应用于Fragment的使用上. LinearLayout: 线性布局,可以是竖排或水平 ...

  6. 洛谷 P1678 烦恼的高考志愿

    题目背景 计算机竞赛小组的神牛V神终于结束了万恶的高考,然而作为班长的他还不能闲下来,班主任老t给了他一个艰巨的任务:帮同学找出最合理的大学填报方案.可是v神太忙了,身后还有一群小姑娘等着和他约会,于 ...

  7. Mining Your Own Business UVALive - 5135(点双联通分量)

    these days I‘m tired!,but very happy... #include<cstdio> #include<cstring> #include<s ...

  8. Android开发性能优化总结(一)

    安卓开发应用首先要讲究良好的用户体验,如果一款软件卡顿现象严重,不流畅,经常崩溃,那么将给用户带来极不良好的体验,从而损失用户. 在实际开发和学习中,我总结了一下关于安卓性能的优化,供大家参考交流. ...

  9. IT(然而其实是。。hdu5244?)

    Time Limit: 3000 ms Memory Limit: 256 MB Description IT = Inverse Transform 两个长度为 \(2^n\) 的序列 \(a,b\ ...

  10. Linux系统之路——用CentOS 7打造合适的科研环境

    安装CentOS CentOS 7的安装与其他Linux发行版的安装差不多,个别地方稍有不同. 准备工作 准备材料 U盘:容量700M以上,用于制作U盘启动盘,因为在制作启动盘时会格式化U盘,所以U盘 ...