zoukankan      html  css  js  c++  java
  • Android中Linux suspend/resume流程

    Android中Linux suspend/resume流程
    首先我们从linux kernel 的suspend说起,不管你是使用echo mem > /sys/power/state 或者使用你的开发板已经拥有的power key 都可以实现系统进入suspend的功能,这是suspend的基础,即控制系统使suspend得到执行的机会,这里相信大家都可以理解,不再过多说明。

    那么suspend得到了执行的机会又是怎么一步一步开始往下执行的呢?现在就开始我们的系统的电源管理之旅:

    我们就通过echo mem > /sys/power/state这种方式来看,这样更容易被理解,位于/sys/power下面的这个state,做driver不知道那可说不过去,我们就看看这个state是在哪个地方创建的吧

    kernel/kernel/power/suspend.c

    [html] view plaincopyprint?
    static int __init pm_init(void) 

        int error = pm_start_workqueue(); 
        if (error) 
            return error; 
        hibernate_image_size_init(); 
        hibernate_reserved_size_init(); 
        power_kobj = kobject_create_and_add("power", NULL); 
        if (!power_kobj) 
            return -ENOMEM; 
        return sysfs_create_group(power_kobj, &attr_group); 

     
    core_initcall(pm_init); 

    static int __init pm_init(void)
    {
     int error = pm_start_workqueue();
     if (error)
      return error;
     hibernate_image_size_init();
     hibernate_reserved_size_init();
     power_kobj = kobject_create_and_add("power", NULL);
     if (!power_kobj)
      return -ENOMEM;
     return sysfs_create_group(power_kobj, &attr_group);
    }

    core_initcall(pm_init);
    这段代码很少却很重要,我关心的是他确实为我们在sys目录下先建了一个power目录,然后,return时创建了很多接口,其中一个就是state,以下是接口定义

    [html] view plaincopyprint?
    static struct attribute * g[] = { 
        &state_attr.attr, 
    #ifdef CONFIG_PM_TRACE 
        &pm_trace_attr.attr, 
        &pm_trace_dev_match_attr.attr, 
    #endif 
    #ifdef CONFIG_PM_SLEEP 
        &pm_async_attr.attr, 
        &wakeup_count_attr.attr, 
    #ifdef CONFIG_PM_DEBUG 
        &pm_test_attr.attr, 
    #endif 
    #ifdef CONFIG_USER_WAKELOCK 
        &wake_lock_attr.attr, 
        &wake_unlock_attr.attr, 
    #endif 
    #endif 
        NULL, 
    }; 
     
    static struct attribute_group attr_group = { 
        .attrs = g, 
    }; 

    static struct attribute * g[] = {
     &state_attr.attr,
    #ifdef CONFIG_PM_TRACE
     &pm_trace_attr.attr,
     &pm_trace_dev_match_attr.attr,
    #endif
    #ifdef CONFIG_PM_SLEEP
     &pm_async_attr.attr,
     &wakeup_count_attr.attr,
    #ifdef CONFIG_PM_DEBUG
     &pm_test_attr.attr,
    #endif
    #ifdef CONFIG_USER_WAKELOCK
     &wake_lock_attr.attr,
     &wake_unlock_attr.attr,
    #endif
    #endif
     NULL,
    };

    static struct attribute_group attr_group = {
     .attrs = g,
    };上面你可以看到了这些接口了

    我们在echo mem > /sys/power/state,或调用的我们的接口函数state_store,suspend也就才真正开始走出第一步


    [html] view plaincopyprint?
    static ssize_t state_store(struct kobject *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); 
        len = p ? p - buf : n; 
     
        /* First, check if we are requested to 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) 
    #ifdef CONFIG_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; 

    static ssize_t state_store(struct kobject *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);
     len = p ? p - buf : n;

     /* First, check if we are requested to 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)
    #ifdef CONFIG_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;
    }这里我们echo mem > /sys/power/state, 还有一种echo on > /sys/power/state,接着state_store进入reauest_suspend_state(state),然后如果是on的话进入late_resume_work(在执行late_resume_work之前会向系统申请main_wake_lock),如果是mem进入early_suspend_work。

    reauest_suspend_state函数路径:kernel/kernel/power/earlysuspend.c

    [html] view plaincopyprint?
    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) ", 
                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); 

    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) ",
       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_work,还是late_resume_work。这里我们走early_suspend_work这个分支接着往下看。先看看early_suspend_work怎么被调用

    queue_work(suspend_work_queue, &early_suspend_work);


    这是一个工作队列的调用方法,找到early_suspend_work的定义

    static DECLARE_WORK(early_suspend_work, early_suspend);


    这里有关于工作队列的方法,不知道就要自己去看看了,所以这里最终调用的其实是early_suspend这个方法


    [html] view plaincopyprint?
    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 ", state); 
            mutex_unlock(&early_suspend_lock); 
            goto abort; 
        } 
     
        if (debug_mask & DEBUG_SUSPEND) 
            pr_info("early_suspend: call handlers "); 
        list_for_each_entry(pos, &early_suspend_handlers, link) { 
            if (pos->suspend != NULL) { 
                if (debug_mask & DEBUG_VERBOSE) 
                    pr_info("early_suspend: calling %pf ", pos->suspend); 
                pos->suspend(pos); 
            } 
        } 
        mutex_unlock(&early_suspend_lock); 
     
        if (debug_mask & DEBUG_SUSPEND) 
            pr_info("early_suspend: sync "); 
     
        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); 

    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 ", state);
      mutex_unlock(&early_suspend_lock);
      goto abort;
     }

     if (debug_mask & DEBUG_SUSPEND)
      pr_info("early_suspend: call handlers ");
     list_for_each_entry(pos, &early_suspend_handlers, link) {
      if (pos->suspend != NULL) {
       if (debug_mask & DEBUG_VERBOSE)
        pr_info("early_suspend: calling %pf ", pos->suspend);
       pos->suspend(pos);
      }
     }
     mutex_unlock(&early_suspend_lock);

     if (debug_mask & DEBUG_SUSPEND)
      pr_info("early_suspend: sync ");

     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);
    }

    early_suspend()这个函数里会遍历early_suspend_handlers,依次执行里面的early_suspend函数,执行完所有的early_suspend后,释放main_wake_lock,进入wake_unlock函数。

    wake_unlock(&main_wake_lock);


    这里还是说一下吧,这个main_wake_lock是个什么东西,路径:kernel/kernel/power/wakelock.c

    struct wake_lock main_wake_lock;


    看他的初始化

    wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");

    wake_lock(&main_wake_lock);


    首先初始化,然后lock,等待unlock

    对于一个lock进入wake_unlock,首先会将lock从原链表中删除(active_wake_locks),然后加入inactive_locks链表中。


    [html] view plaincopyprint?
    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 ", 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) { 
            long has_lock = has_wake_lock_locked(type); 
            if (has_lock > 0) { 
                if (debug_mask & DEBUG_EXPIRE) 
                    pr_info("wake_unlock: %s, start expire timer, " 
                        "%ld ", 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 ", 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); 

    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 ", 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) {
      long has_lock = has_wake_lock_locked(type);
      if (has_lock > 0) {
       if (debug_mask & DEBUG_EXPIRE)
        pr_info("wake_unlock: %s, start expire timer, "
         "%ld ", 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 ", 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);
    }
    对于释放锁,上面两个过程就结束了,但是如果这个锁的类型是WAKE_LOCK_SUSPEND,那么还需要执行一些操作,判断是否可以进入睡眠。首先调has_wake_lock_locked(type)去查找是否还有这种类型的锁,会遍历active_wake_locks[type]链表,如果在这个链表中一检测中有锁,而且该锁不是超时锁,那么就返回-1。如果是超时锁,且已经超时了,那就去释放这个锁,如果没超时就得到一个max_timeout,然后返回max_timeout。接着就会回到wake_unlock函数中,调用mod_timer(&expire_timer,jiffies +has_lock);has_lock就是前面返回的max_timeout,这句话的意思就是向系统中再添加定时器,定时时间就是最大的超时时间.expire_timer的操作函数是expire_wake_locks,这里会去检测还有没有锁,没有的话就进入suspend_work,执行suspend,进入睡眠流程。上面wake_unlock中如果没有检测到锁,也会执行suspend。在suspend函数中又会通过has_wake_lock去检测有没有锁,有锁就直接返回。

    queue_work(suspend_work_queue, &suspend_work);


    又是一个工作队列,看看他的定义,找到他的处理过程

    static DECLARE_WORK(suspend_work, suspend);


    所以他真正执行的是suspend这个方法


    [html] view plaincopyprint?
    static void suspend(struct work_struct *work) 

        int ret; 
        int entry_event_num; 
        struct timespec ts_entry, ts_exit; 
     
        if (has_wake_lock(WAKE_LOCK_SUSPEND)) { 
            if (debug_mask & DEBUG_SUSPEND) 
                pr_info("suspend: abort suspend "); 
            return; 
        } 
     
        entry_event_num = current_event_num; 
        sys_sync(); 
        if (debug_mask & DEBUG_SUSPEND) 
            pr_info("suspend: enter suspend "); 
        getnstimeofday(&ts_entry); 
        ret = pm_suspend(requested_suspend_state); 
        getnstimeofday(&ts_exit); 
     
        if (debug_mask & DEBUG_EXIT_SUSPEND) { 
            struct rtc_time tm; 
            rtc_time_to_tm(ts_exit.tv_sec, &tm); 
            pr_info("suspend: exit suspend, ret = %d " 
                "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC) ", ret, 
                tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 
                tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec); 
        } 
     
        if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) { 
            ++suspend_short_count; 
     
            if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) { 
                suspend_backoff(); 
                suspend_short_count = 0; 
            } 
        } else { 
            suspend_short_count = 0; 
        } 
     
        if (current_event_num == entry_event_num) { 
            if (debug_mask & DEBUG_SUSPEND) 
                pr_info("suspend: pm_suspend returned with no event "); 
            wake_lock_timeout(&unknown_wakeup, HZ / 2); 
        } 

    static void suspend(struct work_struct *work)
    {
     int ret;
     int entry_event_num;
     struct timespec ts_entry, ts_exit;

     if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
      if (debug_mask & DEBUG_SUSPEND)
       pr_info("suspend: abort suspend ");
      return;
     }

     entry_event_num = current_event_num;
     sys_sync();
     if (debug_mask & DEBUG_SUSPEND)
      pr_info("suspend: enter suspend ");
     getnstimeofday(&ts_entry);
     ret = pm_suspend(requested_suspend_state);
     getnstimeofday(&ts_exit);

     if (debug_mask & DEBUG_EXIT_SUSPEND) {
      struct rtc_time tm;
      rtc_time_to_tm(ts_exit.tv_sec, &tm);
      pr_info("suspend: exit suspend, ret = %d "
       "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC) ", ret,
       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
       tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);
     }

     if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {
      ++suspend_short_count;

      if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {
       suspend_backoff();
       suspend_short_count = 0;
      }
     } else {
      suspend_short_count = 0;
     }

     if (current_event_num == entry_event_num) {
      if (debug_mask & DEBUG_SUSPEND)
       pr_info("suspend: pm_suspend returned with no event ");
      wake_lock_timeout(&unknown_wakeup, HZ / 2);
     }
    }

    suspend函数中,通过pm_suspend(requested_suspend_state)进入suspend操作。这个里面也有唤醒操作,只有等唤醒后才会跳出pm_suspend,跳出后会打印log:suspend:exit suspend, ret =pm_suspend就是判断传入的state是否符合suspend,符合就调用enter_state(state),到现在开始才进入了linux标准的suspend流程。

    pm_suspend的路径:kernel/kernel/power/suspend.c


    [html] view plaincopyprint?
    int pm_suspend(suspend_state_t state) 

        if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX) 
            return enter_state(state); 
        return -EINVAL; 

    EXPORT_SYMBOL(pm_suspend); 

    int pm_suspend(suspend_state_t state)
    {
     if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX)
      return enter_state(state);
     return -EINVAL;
    }
    EXPORT_SYMBOL(pm_suspend);
    enter_state这个函数主要有三个函数调用,分别是suspend_prepare,suspend_devices_and_enter,suspend_finish。


    [html] view plaincopyprint?
    /** 
     *  enter_state - Do common work of entering low-power state. 
     *  @state:     pm_state structure for state we're entering. 
     * 
     *  Make sure we're the only ones trying to enter a sleep state. Fail 
     *  if someone has beat us to it, since we don't want anything weird to 
     *  happen when we wake up. 
     *  Then, do the setup for suspend, enter the state, and cleaup (after 
     *  we've woken up). 
     */ 
    int enter_state(suspend_state_t state) 

        int error; 
     
        if (!valid_state(state)) 
            return -ENODEV; 
     
        if (!mutex_trylock(&pm_mutex)) 
            return -EBUSY; 
     
        printk(KERN_INFO "PM: Syncing filesystems ... "); 
        sys_sync(); 
        printk("done. "); 
     
        pr_debug("PM: Preparing system for %s sleep ", pm_states[state]); 
        error = suspend_prepare(); 
        if (error) 
            goto Unlock; 
     
        if (suspend_test(TEST_FREEZER)) 
            goto Finish; 
     
        pr_debug("PM: Entering %s sleep ", pm_states[state]); 
        pm_restrict_gfp_mask(); 
        error = suspend_devices_and_enter(state); 
        pm_restore_gfp_mask(); 
     
     Finish: 
        pr_debug("PM: Finishing wakeup. "); 
        suspend_finish(); 
     Unlock: 
        mutex_unlock(&pm_mutex); 
        return error; 

    /**
     * enter_state - Do common work of entering low-power state.
     * @state:  pm_state structure for state we're entering.
     *
     * Make sure we're the only ones trying to enter a sleep state. Fail
     * if someone has beat us to it, since we don't want anything weird to
     * happen when we wake up.
     * Then, do the setup for suspend, enter the state, and cleaup (after
     * we've woken up).
     */
    int enter_state(suspend_state_t state)
    {
     int error;

     if (!valid_state(state))
      return -ENODEV;

     if (!mutex_trylock(&pm_mutex))
      return -EBUSY;

     printk(KERN_INFO "PM: Syncing filesystems ... ");
     sys_sync();
     printk("done. ");

     pr_debug("PM: Preparing system for %s sleep ", pm_states[state]);
     error = suspend_prepare();
     if (error)
      goto Unlock;

     if (suspend_test(TEST_FREEZER))
      goto Finish;

     pr_debug("PM: Entering %s sleep ", pm_states[state]);
     pm_restrict_gfp_mask();
     error = suspend_devices_and_enter(state);
     pm_restore_gfp_mask();

     Finish:
     pr_debug("PM: Finishing wakeup. ");
     suspend_finish();
     Unlock:
     mutex_unlock(&pm_mutex);
     return error;
    }

    suspend_prepare做一些睡眠的准备工作

    suspend_devices_and_enter就是真正的设备进入睡眠

    suspend_finish唤醒后进行的操作。

    下面来一个一个分析:

    suspend_prepare中首先通过pm_prepare_console,给suspend分配一个虚拟终端来输出信息;接着通过pm_notifier_call_chain来广播一个系统进入suspend的通报;关闭用户态的helper进程;最后通过suspend_freeze_processes来冻结用户态进程,最后会尝试释放一些内存。在suspend_freeze_processes()函数中调用了freeze_processes()函数,而freeze_processes()函数中又调用了try_to_freeze_tasks()来完成冻结任务。在冻结过程中,会判断当前进程是否有wake_lock,若有,则冻结失败,函数会放弃冻结。

    执行完上面的操作后再次回到enter_state函数中,下面开始调用suspend_devices_and_enter()函数让外设进入休眠。在suspend_devices_and_enter()中首先调用关于平台的suspend_ops->begin,接着通过suspend_console来关闭console,也可以通过改变一个flag来使这个函数无效。接着调用dpm_suspend_start。dpm_suspend_start中会执行device_prepare和device_suspend,这两个函数都是调用pm接口里的prepare和suspend函数(其实这里就开始通过总线的接口来执行驱动的suspend函数了,通过bus->pm->suspend)。接着回到suspend_devices_and_enter中调用suspend_enter(state);在suspend_enter中,首先调用平台相关的suspend_ops->prepare,接着执行dpm_suspend_noirq()调用pm接口里的pm->suspend_noirq,回到suspend_enter,接着调用suspend_ops->prepare_late,接下来多cpu中非启动的cpu通过函数disable_nonboot_cpus()被关闭,然后通过调用arch_suspend_disable_irqs()关闭本地中断。再后来才到睡眠设备的操作,sysdev_suspend(PMSG_SUSPEND),这样就会进入sysdev_driver.suspend阶段。最后调用suspend_ops->enter(),这里就开始执行到睡眠的最后一步了,执行平台相关的睡眠。在平台睡眠的代码中主要是通过suspend_in_iram(suspend_param1)来执行一段汇编代码,最终在汇编中睡死。唤醒的步骤与睡眠的步骤相反,cpu有电后会首先从汇编中起来,接着回到suspend_enter函数中,执行suspend_ops->enter()返回后的一些唤醒代码,这边就不再去说了,基本是按照上面的逆序来操作的。

    上面的过程在我看来还是很复杂的,power management 要好好研究一下了

    resume的过程

    唤醒的时候,程序从suspend_devices_and_enter函数中出来后,开始执行suspend_finish,接着就会从enter_state中退出来,返回pm_suspend,然后又从pm_suspend返回到wakelock.c中的suspend(),在这里接下来就会打印出”suspend:exit suspend, ret“这些log。

      

  • 相关阅读:
    [转]Spring.NET企业架构实践之 NHibernate + Spring.NET + WCF + Windows服务 + Silverlight 中小企业应用架构完整Demo
    [转]用EA 建模设计Blog全过程
    下载页面
    Expression Studio 4 ultimate x86 下载含序列号
    ACS.NET缓存技术
    Case
    再谈C#中的委托,匿名方法和Lambda表达式
    代理服务器的设置
    软件架构师之职责范围和高级软件工程师的职责范围
    WPF博客
  • 原文地址:https://www.cnblogs.com/ITlearning/p/3163496.html
Copyright © 2011-2022 走看看