zoukankan      html  css  js  c++  java
  • Android中休眠与唤醒之wake_lock, early_suspend, late_resume

          最近研究如何让Android不休眠。听组里人说,机器在充电的时候不休眠。我试了一下,确实是,串口可以使用(CONFIG_PM_DEBUG并没有打开)。

    这个时候,LCD显示屏是休眠了,触摸屏也休眠了,其他的比如重力传感器等就没有看了,但是标准的Linux系统并没有进入休眠。看了网上好多关于Android系统的休眠与唤醒

    例子,感觉有些懵懵懂懂的。于是,还是看内核代码吧。

            Android在标准的Linux休眠与唤醒机制上又加了一层,就是early_suspend / late_resume。顾名思意,使用early_suspend()进行休眠的设备,它休眠的时刻早于其他设备,使用late_resume()唤醒的设备,它被唤醒的时刻要晚于其他设备。这对函数通常成对出现,当内核打开了CONFIG_EARLY_SUSPEND(Android默认打开)后,就可以使

    用这组函数来代替驱动中标准的 suspend / resume接口。

            好了,讲到early_suspend和late_resume,似乎必须要扯到一种叫做wake_lock的锁定机制了。其实,单纯从某个设备的驱动程序上来讲,未必需要用到wake_lock机制,

    比如我们的触摸屏驱动中使用了early_suspend,就没有使用wake_lock.

           目前,我了解到的,wake_lock的用途只有一个,那就是防止系统进入休眠(这里的休眠,指的是标准的Linux的休眠,不包含使用early_suspend()进行休眠的设备,

    使用early_suspend()的设备,在系统还有wake_lock锁的时候,也是要休眠的)。

           好吧,现在是时候分析下Android/Linux的休眠与唤醒了,虽然好多先人 都已经讲了这些,而且讲的还不错,这里我还是要提一下。

    root@android:/ # ls /sys/power/                                                
    pm_async
    state
    wait_for_fb_sleep
    wait_for_fb_wake
    wake_lock
    wake_unlock
    wakeup_count

           这里,我只关注state,当state 的值变化时,内核会调用

    1. static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,  
    2.                const char *buf, size_t n)  
    3. {  
    4. #ifdef CONFIG_SUSPEND  
    5. #ifdef CONFIG_EARLYSUSPEND  
    6.     suspend_state_t state = PM_SUSPEND_ON;  
    7. #else  
    8.     suspend_state_t state = PM_SUSPEND_STANDBY;  
    9. #endif  
    10.     const char * const *s;   
    11. #endif  
    12.     char *p;   
    13.     int len;  
    14.     int error = -EINVAL;  
    15.   
    16.     p = memchr(buf, ' ', n);   
    17.     len = p ? p - buf : n;  
    18.   
    19.     /* First, check if we are requested to hibernate */  
    20.     if (len == 4 && !strncmp(buf, "disk", len)) {  
    21.         error = hibernate();  
    22.   goto Exit;  
    23.     }     
    24.   
    25. #ifdef CONFIG_SUSPEND  
    26.     for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {  
    27.         if (*s && len == strlen(*s) && !strncmp(buf, *s, len))  
    28.             break;  
    29.     }     
    30.     if (state < PM_SUSPEND_MAX && *s)   
    31. #ifdef CONFIG_EARLYSUSPEND  
    32.         if (state == PM_SUSPEND_ON || valid_state(state)) {  
    33.             error = 0;  
    34.             request_suspend_state(state);//这里,进入了Android的休眠与唤醒的处理函数  
    35.         }  
    36. #else  
    37.         error = enter_state(state);  
    38. #endif  
    39. #endif  
    40.   
    41.  Exit:  
    42.     return error ? error : n;  
    43. }  
    44.   
    45. power_attr(state);  


    看看
    1. request_suspend_state()都干了些什么事情  

    1. void request_suspend_state(suspend_state_t new_state)  
    2. {  
    3.     unsigned long irqflags;  
    4.     int old_sleep;  
    5.   
    6.     spin_lock_irqsave(&state_lock, irqflags);  
    7.     old_sleep = state & SUSPEND_REQUESTED;  
    8.     if (debug_mask & DEBUG_USER_STATE) {  
    9.         struct timespec ts;   
    10.         struct rtc_time tm;   
    11.         getnstimeofday(&ts);  
    12.         rtc_time_to_tm(ts.tv_sec, &tm);  
    13.         pr_info("request_suspend_state: %s (%d->%d) at %lld "  
    14.             "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC) ",  
    15.             new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",  
    16.             requested_suspend_state, new_state,  
    17.             ktime_to_ns(ktime_get()),  
    18.             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,  
    19.             tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);  
    20.     }     
    21.     if (!old_sleep && new_state != PM_SUSPEND_ON) {  
    22.         state |= SUSPEND_REQUESTED;  
    23.         queue_work(suspend_work_queue, &early_suspend_work);//在休眠的时候,去遍历执行early_suspend_work这个队列  
    24.     } else if (old_sleep && new_state == PM_SUSPEND_ON) {  
    25.         state &= ~SUSPEND_REQUESTED;  
    26.         wake_lock(&main_wake_lock);  
    27.         queue_work(suspend_work_queue, &late_resume_work);//在唤醒的时候,去遍历执行late_resume_work这个队列  
    28.     }     
    29.     requested_suspend_state = new_state;  
    30.     spin_unlock_irqrestore(&state_lock, irqflags);  
    31. }  

            怎么样,是不是很简单,根据用户/系统所请求的状态,去做相应的动作(休眠/唤醒)

    能用到的一些变量的声明在这里

    1. static void early_suspend(struct work_struct *work);  
    2. static void late_resume(struct work_struct *work);  
    3. static DECLARE_WORK(early_suspend_work, early_suspend);  
    4. static DECLARE_WORK(late_resume_work, late_resume);  
             看名字也知道了,early_suspend这个函数指针来处理early_suspend_work这条队列,late_resume 这个函数指针来处理late_resume_work这条队列。

             虽然函数early_suspend()和late_resume()的实现都非常易懂,这里还是要贴出来,因为还有些东西要分析一下。

    1. static void early_suspend(struct work_struct *work)  
    2. {  
    3.     struct early_suspend *pos;  
    4.     unsigned long irqflags;  
    5.     int abort = 0;  
    6.   
    7.     mutex_lock(&early_suspend_lock);  
    8.     spin_lock_irqsave(&state_lock, irqflags);  
    9.     if (state == SUSPEND_REQUESTED)  
    10.         state |= SUSPENDED;  
    11.     else  
    12.         abort = 1;  
    13.     spin_unlock_irqrestore(&state_lock, irqflags);  
    14.   
    15.     if (abort) {  
    16.         if (debug_mask & DEBUG_SUSPEND)  
    17.             pr_info("early_suspend: abort, state %d ", state);  
    18.         mutex_unlock(&early_suspend_lock);  
    19.         goto abort;  
    20.     }  
    21.   
    22.     if (debug_mask & DEBUG_SUSPEND)  
    23.         pr_info("early_suspend: call handlers ");  
    24.     list_for_each_entry(pos, &early_suspend_handlers, link) {//这里就是关键了,遍历early_suspend_handler这条链表(在驱动中注册early_suspend的时候,都注册到这条链表上了)  
    25.         if (pos->suspend != NULL) {  
    26.             if (debug_mask & DEBUG_VERBOSE)  
    27.                 pr_info("early_suspend: calling %pf ", pos->suspend);  
    28.             pos->suspend(pos);//调用各个实现进行各设备的休眠  
    29.         }  
    30.     }  
    31.     mutex_unlock(&early_suspend_lock);  
    32.   
    33.     if (debug_mask & DEBUG_SUSPEND)  
    34.         pr_info("early_suspend: sync ");  
    35.   
    36.     sys_sync();  
    37. abort:  
    38.     spin_lock_irqsave(&state_lock, irqflags);  
    39.     if (state == SUSPEND_REQUESTED_AND_SUSPENDED)  
    40.         wake_unlock(&main_wake_lock);//这里很重要,别小看这个一个wake_unlock,起初我也以为这仅仅是一个释放main锁,其实里面有玄机呢。还记得wake_lock主要用来干嘛么,用来防止系统休眠,也就是说,只要系统中其他地方还拥有wake_lock锁(类型WAKE_LOCK_SUSPEND),系统就没法进入休眠,如果没有锁了,那就要接着走标准Linux的那一套休眠机制了  
    41.     spin_unlock_irqrestore(&state_lock, irqflags);  
    42. }  
    1.   
    先跳过late_resume()。来看下wake_unlock()的实现吧
    1. void wake_unlock(struct wake_lock *lock)  
    2. {  
    3.     int type;  
    4.     unsigned long irqflags;  
    5.     spin_lock_irqsave(&list_lock, irqflags);  
    6.     type = lock->flags & WAKE_LOCK_TYPE_MASK;  
    7. #ifdef CONFIG_WAKELOCK_STAT  
    8.     wake_unlock_stat_locked(lock, 0);   
    9. #endif  
    10.     if (debug_mask & DEBUG_WAKE_LOCK)  
    11.         pr_info("wake_unlock: %s ", lock->name);  
    12.     lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);  
    13.     list_del(&lock->link);  
    14.     list_add(&lock->link, &inactive_locks);  
    15.     if (type == WAKE_LOCK_SUSPEND) {//类型,驱动中一般只有这一种类型  
    16.         long has_lock = has_wake_lock_locked(type);  
    17.         if (has_lock > 0) {  
    18.             if (debug_mask & DEBUG_EXPIRE)  
    19.                 pr_info("wake_unlock: %s, start expire timer, "  
    20.                     "%ld ", lock->name, has_lock);  
    21.             mod_timer(&expire_timer, jiffies + has_lock);  
    22.         } else {  
    23.             if (del_timer(&expire_timer))  
    24.                 if (debug_mask & DEBUG_EXPIRE)  
    25.                     pr_info("wake_unlock: %s, stop expire "  
    26.                         "timer ", lock->name);  
    27.             if (has_lock == 0)//如果没有锁了,要进入标准Linux的休眠机制了,咱们接着往下跟  
    28.                 queue_work(suspend_work_queue, &suspend_work);  
    29.         }     
    30.         if (lock == &main_wake_lock) {  
    31.             if (debug_mask & DEBUG_SUSPEND)  
    32.                 print_active_locks(WAKE_LOCK_SUSPEND);  
    33. #ifdef CONFIG_WAKELOCK_STAT  
    34.             update_sleep_wait_stats_locked(0);  
    35. #endif  
    36.         }     
    37.     }     
    38.     spin_unlock_irqrestore(&list_lock, irqflags);  
    39. }  
    40. EXPORT_SYMBOL(wake_unlock);  


    这里就是进入标准Linux的休眠的地方了
    1. static void suspend(struct work_struct *work)  
    2. {  
    3.     int ret;  
    4.     int entry_event_num;  
    5.     struct timespec ts_entry, ts_exit;  
    6.   
    7.     if (has_wake_lock(WAKE_LOCK_SUSPEND)) {  
    8.         if (debug_mask & DEBUG_SUSPEND)  
    9.             pr_info("suspend: abort suspend ");  
    10.         return;  
    11.     }  
    12.   
    13.     entry_event_num = current_event_num;  
    14.     sys_sync();  
    15.     if (debug_mask & DEBUG_SUSPEND)  
    16.         pr_info("suspend: enter suspend ");  
    17.     getnstimeofday(&ts_entry);  
    18.     ret = pm_suspend(requested_suspend_state);//这里是关键点  
    19.     getnstimeofday(&ts_exit);  
    20.   
    21.     if (debug_mask & DEBUG_EXIT_SUSPEND) {  
    22.         struct rtc_time tm;  
    23.         rtc_time_to_tm(ts_exit.tv_sec, &tm);  
    24.         pr_info("suspend: exit suspend, ret = %d "  
    25.             "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC) ", ret,  
    26.             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,  
    27.             tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);  
    28.     }  
    29.   
    30.     if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {  
    31.         ++suspend_short_count;  
    32.   
    33.         if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {  
    34.             suspend_backoff();  
    35.             suspend_short_count = 0;  
    36.         }  
    37.     } else {  
    38.         suspend_short_count = 0;  
    39.     }  
    40.   
    41.     if (current_event_num == entry_event_num) {  
    42.         if (debug_mask & DEBUG_SUSPEND)  
    43.             pr_info("suspend: pm_suspend returned with no event ");  
    44.         wake_lock_timeout(&unknown_wakeup, HZ / 2);  
    45.     }  
    46. }  
    47. static DECLARE_WORK(suspend_work, suspend);  

    1. int pm_suspend(suspend_state_t state)  
    2. {  
    3.     if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX)  
    4.         return enter_state(state);//正如你所料,开始走Linux那套休眠的流程了  
    5.     return -EINVAL;  
    6. }  
    7. EXPORT_SYMBOL(pm_suspend);  

           唤醒相关的代码就不贴 了,跟休眠类似的。

    下面讲下驱动中如何使用wake_lock和early_suspend,总的来说,还是挺简单的

    比如在设备probe的时候做如下操作

    struct early_suspend    early_suspend;

    early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; //等级,等级大小和suspend顺序一致,和resume顺序相反
    early_suspend.suspend = xxx_early_suspend;//指定函数指针,需自己实现
    early_suspend.resume = xxx_late_resume;

    register_early_suspend(&early_suspend);//注册进核心,也就是加入刚才early_suspend_handlers那个链表


    struct wake_lock    chrg_lock;
    wake_lock_init(&chrg_lock, WAKE_LOCK_SUSPEND, "xxx_wake_lock");//初始化类型为WAKE_LOCK_SUSPEND的wake_lock锁

    #ifdef CONFIG_HAS_EARLYSUSPEND
    static void xxx_early_suspend(struct early_suspend *h)
    {
           ....
            wake_lock(&chrg_lock);
          ....
    }


    static void xxx_late_resume(struct early_suspend *h)
    {
         .....
            wake_unlock(&chrg_lock);
         ....
    }
    #endif


     
  • 相关阅读:
    【转】编写高质量代码改善C#程序的157个建议——建议41:实现标准的事件模型
    【转】编写高质量代码改善C#程序的157个建议——建议40:使用event关键字为委托施加保护
    【转】编写高质量代码改善C#程序的157个建议——建议39:了解委托的实质
    【转】编写高质量代码改善C#程序的157个建议——建议38:小心闭包中的陷阱
    【转】编写高质量代码改善C#程序的157个建议——建议37:使用Lambda表达式代替方法和匿名方法
    7.FactoryBean 和BeanFactory去区别
    6.2-SingletonBeanRegistry-DefaultSingletonBeanRegistry
    6.1-AliasRegistry
    ConfigurableBeanFactory
    4.AutowireCapableBeanFactory 自动装配工厂
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744581.html
Copyright © 2011-2022 走看看