Android Suspend

Android Suspend
当用户写入mem 或者 standby到 /sys/power/state中的时候, state_store()会被调用, 然后Android会在这里调用 request_suspend_state() 而标准的Linux会在这里进入enter_state()这个函数. 如果请求的是休眠, 那么early_suspend这个workqueue就会被调用,并且进入early_suspend状态.

void request_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
在early_suspend()函数中, 首先会检查现在请求的状态还是否是suspend, 来防止suspend的请求会在这个时候取消掉(因为这个时候用户进程还在运行),如 果需要退出, 就简单的退出了. 如果没有, 这个函数就会把early suspend中 注册的一系列的回调都调用一次, 然后同步文件系统, 然后放弃掉 main_wake_lock, 这个wake lock是一个没有超时的锁,如果这个锁不释放, 那 么系统就无法进入休眠.

   static void early_suspend(struct work_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: call handlers/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);}Late Resume
当所有的唤醒已经结束以后, 用户进程都已经开始运行了, 唤醒通常会是以下的几种原因:

来电
如果是来电, 那么Modem会通过发送命令给rild来让rild通知WindowManager有来电响应,这样就会远程调用PowerManagerService来写"on" 到 /sys/power/state 来执行late resume的设备, 比如点亮屏幕等.

用户按键用户按键事件会送到WindowManager中, WindowManager会处理这些 按键事件,按键分为几种情况, 如果案件不是唤醒键(能够唤醒系统的按键) 那么WindowManager会主动放弃wakeLock来使系统进入再次休眠, 如果按键是唤醒键,那么WindowManger就会调用PowerManagerService中的接口来执行 Late Resume.
Late Resume 会依次唤醒前面调用了Early Suspend的设备.
static void late_resume(struct work_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: call handlers/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
我们接下来看一看wake lock的机制是怎么运行和起作用的, 主要关注 wakelock.c文件就可以了.

wake lock 有加锁和解锁两种状态, 加锁的方式有两种, 一种是永久的锁住, 这样的锁除非显示的放开, 是不会解锁的, 所以这种锁的使用是非常小心的. 第二种是超时锁, 这种锁会锁定系统唤醒一段时间, 如果这个时间过去了, 这个锁会自动解除.

锁有两种类型:

WAKE_LOCK_SUSPEND 这种锁会防止系统进入睡眠
WAKE_LOCK_IDLE 这种锁不会影响系统的休眠, 作用我不是很清楚.
在wake lock中, 会有3个地方让系统直接开始suspend(), 分别是:

在wake_unlock()中, 如果发现解锁以后没有任何其他的wake lock了, 就开始休眠
在定时器都到时间以后, 定时器的回调函数会查看是否有其他的wake lock, 如果没有, 就在这里让系统进入睡眠.
在wake_lock() 中, 对一个wake lock加锁以后, 会再次检查一下有没有锁, 我想这里的检查是没有必要的, 更好的方法是使加锁的这个操作原子化, 而 不是繁冗的检查. 而且这样的检查也有可能漏掉.
Suspend
当wake_lock 运行 suspend()以后, 在wakelock.c的suspend()函数会被调用,这个函数首先sync文件系统,然后调用pm_suspend(request_suspend_state),接下来pm_suspend()就会调用enter_state()来进入Linux的休眠流程..

      static void suspend(struct work_struct *work){        int ret;        int entry_event_num;        if (has_wake_lock(WAKE_LOCK_SUSPEND)) {                if (debug_mask & DEBUG_SUSPEND)                        pr_info("suspend: abort suspend/n");                return;        }        entry_event_num = current_event_num;        sys_sync();        if (debug_mask & DEBUG_SUSPEND)                pr_info("suspend: enter suspend/n");        ret = pm_suspend(requested_suspend_state);        if (current_event_num == entry_event_num) {                wake_lock_timeout(&unknown_wakeup, HZ / 2);        }}Android于标准Linux休眠的区别
pm_suspend() 虽然会调用enter_state()来进入标准的Linux休眠流程,但是还 是有一些区别:

当进入冻结进程的时候, android首先会检查有没有wake lock,如果没有, 才会停止这些进程, 因为在开始suspend和冻结进程期间有可能有人申请了 wake lock,如果是这样, 冻结进程会被中断.
在suspend_late()中, 会最后检查一次有没有wake lock, 这有可能是某种快速申请wake lock,并且快速释放这个锁的进程导致的,如果有这种情况, 这里会返回错误, 整个suspend就会全部放弃.如果pm_suspend()成功了,LOG的输出可以通过在kernel cmd里面增加 "no_console_suspend" 来看到suspend和resume过程中的log输出。

 

 

2、系统Suspendresume的函数流程

取一个例子

 

加入suspendresume

 

mxc_board_init-->mxc_init_bl()-->platform_device_register()-->platform_device_add()-->device_add()-->device_pm_add()-->,最终加入到了dpm_list的链表中,在其中的dpm_suspenddpm_suspend中通过遍历这个链表来进行查看哪个device中包含suspendresume项。

 

 

Linux进入休眠呢?用户可以通过读写sys文件/sys /power/state 是实现控制系统进入休眠. 比如

当状态位PM_SUSPEND_ON的状态的时候,调用request_suspend_state();当满足休眠的状态的时候,调用queue_work(suspend_work_queue,&early_suspend_work),调用了early_suspend,然后在其中通过wake_unlock()启动了expire_timer定时器,当定时时间到了,则执行expire_wake_locks,将suspend_work加入到队列中,分析到这里就可以知道了early_suspend_worksuspend_work这两个队列的先后顺序了,suspend调用了pm_suspend,通过判断当前的状态,选择enter_state(),在enter_state中,经过了suspend_preparesuspend_testsuspend_device_and_enter(),在suspend_device_and_enter中调用了device_suspend来保存状态和结束系统的设备,到了dpm_suspend中结束所有的device
 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值