Android wakelock 学习总结第三篇

转载自:http://slightsnow.blog.chinaunix.net/uid-29269256-id-4093367.html


从这篇开始我就开始对android下的wakelock和suspend是如何结合做些自己的分析,长话短说吧,我们首先看看个函数:

路径如下:

kernel/kernel/power/main.c

这个函数就是Native曾调用的接口程序,比如:state, wakelock , unwakelock,这里都有具体实现,下面我们先分析下wake_lock相关函数,上锁过程:
(注意我只分析store函数 show函数我就不分析了)

456 static ssize_t wake_lock_store(struct kobject *kobj,
457                                struct kobj_attribute *attr,
458                                const char *buf, size_t n)
459 {
460         int error = pm_wake_lock(buf);
461         return error ? error : n;
462 }

这端函数没有什么可说的,就是直接把上层申请的锁名称传到pm_wake_lock中,比如 mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService"); 中的PowerManagerService。我们继续看下pm_wake_lock函数,
183 int pm_wake_lock(const char *buf)
184 {
185         const char *str = buf;
186         struct wakelock *wl;
187         u64 timeout_ns = 0;
188         size_t len;
189         int ret = 0;
190
191         while (*str && !isspace(*str))
192                 str++;
193
194         len = str - buf;
195         if (!len)
196                 return -EINVAL;
197
198         if (*str && *str != '\n') {
199                 /* Find out if there's a valid timeout string appended. */
200                 ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
201                 if (ret)
202                         return -EINVAL;
203         }
204
205         mutex_lock(&wakelocks_lock);
206
207         wl = wakelock_lookup_add(buf, len, true);
208         if (IS_ERR(wl)) {
209                 ret = PTR_ERR(wl);
210                 goto out;
211         }
212         if (timeout_ns) {
213                 u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
214
215                 do_div(timeout_ms, NSEC_PER_MSEC);
216                 __pm_wakeup_event(&wl->ws, timeout_ms);
217         } else {                                                                       
218                 __pm_stay_awake(&wl->ws);
219         }
220
221         wakelocks_lru_most_recent(wl);
222
223  out:
224         mutex_unlock(&wakelocks_lock);
225         return ret;
226 }

这端函数主要功能在于检查这个wakelock是否存在红黑二叉树上,把名字短的放在左叉,名字长的放在右叉,这里是不是跟进程调度的二叉树很像?^_^,然后调用__pm_wakeup_event或者__pm_stay_awake函数,这里的timeout_ms我没有看明白怎么取得的,烦人,以后慢慢看看再。继续跟踪没有timeout_ms的函数吧,另外的函数进入也干了同样的事情,还对timeout进行了一些处理,可能和两种锁有关系哦?一种是带timeout时间的锁,到时会注销,一种是不带时间锁,如果没有显示的unlock就不会放弃锁。
418 void __pm_stay_awake(struct wakeup_source *ws)
419 {
420         unsigned long flags;
421
422         if (!ws)
423                 return;
424
425         spin_lock_irqsave(&ws->lock, flags);
426
427         wakeup_source_report_event(ws);
428         del_timer(&ws->timer);
429         ws->timer_expires = 0;
430
431         spin_unlock_irqrestore(&ws->lock, flags);
432 } 

这个函数主要干的就是调用wakeup_source_report_evet这个函数

01 static void wakeup_source_report_event(struct wakeup_source *ws)
402 {
403         ws->event_count++;
404         /* This is racy, but the counter is approximate anyway. */
405         if (events_check_enabled)
406                 ws->wakeup_count++;
407         
408         if (!ws->active)
409                 wakeup_source_activate(ws);

381 static void wakeup_source_activate(struct wakeup_source *ws)
382 {
383         unsigned int cec;
384
385         ws->active = true;
386         ws->active_count++;
387         ws->last_time = ktime_get();
388         if (ws->autosleep_enabled)
389                 ws->start_prevent_time = ws->last_time;
390
391         /* Increment the counter of events in progress. */
392         cec = atomic_inc_return(&combined_event_count);                                                                                    
393
394         trace_wakeup_source_activate(ws->name, cec);
395 }

这两段函数连续看,会发现,对与一些count进行的++,设置了下wake_lock中的wake_source的一些变量,主要是 对于combined_event_count这个变量进行的原子操作++,(这个变量暂时理解胃是记录有多少个wakelock吧),然后就完活了啊,这样就对一个wakelock进行。

我们再分析下wake_unlock_store这个函数,

473 static ssize_t wake_unlock_store(struct kobject *kobj,
474                                  struct kobj_attribute *attr,
475                                  const char *buf, size_t n)
476 {
477         int error = pm_wake_unlock(buf);
478         return error ? error : n;
479 }

操作一样,把写到节点的字符串传到pm_wake_unlock(buf)函数中
228 int pm_wake_unlock(const char *buf)
229 {
230         struct wakelock *wl;
231         size_t len;
232         int ret = 0;
233
234         len = strlen(buf);
235         if (!len)
236                 return -EINVAL;
237
238         if (buf[len-1] == '\n')
239                 len--;
240
241         if (!len)
242                 return -EINVAL;
243
244         mutex_lock(&wakelocks_lock);
245
246         wl = wakelock_lookup_add(buf, len, false);
247         if (IS_ERR(wl)) {
248                 ret = PTR_ERR(wl);
249                 goto out;
250         }
251         __pm_relax(&wl->ws);
252
253         wakelocks_lru_most_recent(wl);
254         wakelocks_gc();
255
256  out:
257         mutex_unlock(&wakelocks_lock);
258         return ret;
259 }

这里也是在wake_lock二叉树查找wake_lock,然后调用__pm_relax(&wl->ws);这个函数,精髓都在这里,我们跟踪下去
535 void __pm_relax(struct wakeup_source *ws)
536 {
537         unsigned long flags;
538
539         if (!ws)
540                 return;
541
542         spin_lock_irqsave(&ws->lock, flags);
543         if (ws->active)
544                 wakeup_source_deactivate(ws);
545         spin_unlock_irqrestore(&ws->lock, flags);
546 }
547 EXPORT_SYMBOL_GPL(__pm_relax);

看到ws->active了吗?这个在之前上锁的时候我们定义过true哦,然后调用wakeup_source_deactivate这个函数,

478 static void wakeup_source_deactivate(struct wakeup_source *ws)
479 {
480         unsigned int cnt, inpr, cec;
481         ktime_t duration;
482         ktime_t now;
483
484         ws->relax_count++;
485         /*
486          * __pm_relax() may be called directly or from a timer function.
487          * If it is called directly right after the timer function has been
488          * started, but before the timer function calls __pm_relax(), it is
489          * possible that __pm_stay_awake() will be called in the meantime and
490          * will set ws->active.  Then, ws->active may be cleared immediately
491          * by the __pm_relax() called from the timer function, but in such a
492          * case ws->relax_count will be different from ws->active_count.
493          */
494         if (ws->relax_count != ws->active_count) {
495                 ws->relax_count--;
496                 return;
497         }
498
499         ws->active = false;
500
501         now = ktime_get();
502         duration = ktime_sub(now, ws->last_time);
503         ws->total_time = ktime_add(ws->total_time, duration);
504         if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time))
505                 ws->max_time = duration;
506
507         ws->last_time = now;
508         del_timer(&ws->timer);
509         ws->timer_expires = 0;
510
511         if (ws->autosleep_enabled)
512                 update_prevent_sleep_time(ws, now);
513
514         /*
515          * Increment the counter of registered wakeup events and decrement the
516          * couter of wakeup events in progress simultaneously.
517          */
518         cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count);
519         trace_wakeup_source_deactivate(ws->name, cec);
520
521         split_counters(&cnt, &inpr);
522         if (!inpr && waitqueue_active(&wakeup_count_wait_queue))
523                 wake_up(&wakeup_count_wait_queue);
524 }

这个函数前面我们就不看了,主要看下 521行到523行,这里split_counters实现如下
 32 static atomic_t combined_event_count = ATOMIC_INIT(0);
 33
 34 #define IN_PROGRESS_BITS        (sizeof(int) * 4)
 35 #define MAX_IN_PROGRESS         ((1 << IN_PROGRESS_BITS) - 1)
 36
 37 static void split_counters(unsigned int *cnt, unsigned int *inpr)
 38 {
 39         unsigned int comb = atomic_read(&combined_event_count);                                                                            
 40
 41         *cnt = (comb >> IN_PROGRESS_BITS);
 42         *inpr = comb & MAX_IN_PROGRESS;
 43 }

这个原子操作不就是得到了刚才我们++combined_event_count记录的wake_lock数量的变量吗?取得32位数上的高16位数啊,这样的话难道每次都是高16位存的都是新值,而后16位都是旧值吗?这个还需要研究研究!然后我们看下后两行

522         if (!inpr && waitqueue_active(&wakeup_count_wait_queue))
523                 wake_up(&wakeup_count_wait_queue);

这个就是判断wake_locks数,等待队列是否活动,然后唤醒等待队列,这个等待队列到底在哪里了?我们往后再说,这里先卖个关子 哇哈哈!

好了 现在我们wake_lock和unwake_lock都已经说了,但是他们和suspend有毛关系呢?我们继续分析state这个函数。

如果我们执行echo mem > /sys/power/state 会发生什么? 没错,手机屏幕会灭,手机会进入了earlysuspend状态,呵呵,这就是我分析第二片文章为什么要特意说下farmework 到 native  怎么把值写到这个节点的必要性了。下面我们看看函数

301 static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
302                            const char *buf, size_t n)
303 {
304         suspend_state_t state;
305         int error;
306
307         error = pm_autosleep_lock();
308         if (error)
309                 return error;
310
311         if (pm_autosleep_state() > PM_SUSPEND_ON) {
312                 error = -EBUSY;
313                 goto out;
314         }
315
316         state = decode_state(buf, n);
317         if (state < PM_SUSPEND_MAX) {
318 #ifdef CONFIG_EARLYSUSPEND
319                 if (state == PM_SUSPEND_ON || valid_state(state)) {
320                         error = 0;
321                         request_suspend_state(state);
322                 }
323 #else
324                 error = pm_suspend(state);
325 #endif
326
327 }
328         else if (state == PM_SUSPEND_MAX)
329                 error = hibernate();
330         else
331                 error = -EINVAL;
332
333  out:
334         pm_autosleep_unlock();
335         return error ? error : n;
336 }

首先判断系统是不是正在suspend的状态,然后检查写入的值是否有效,如果有效这里分支就是为了支持earlysuspend过能和不带earlysuspend功能,然后调用request_suspend_state(state);这个函数。移动设备默认都带earlysuspend功能

208 void request_suspend_state(suspend_state_t new_state)
209 {
210         unsigned long irqflags;
211         int old_sleep;
212         suspend_state_t prev_state;
213
214         mutex_lock(&suspend_lock);
215         prev_state = suspend_state;
216         spin_lock_irqsave(&state_lock, irqflags);
217         old_sleep = state & SUSPEND_REQUESTED;
218         if (debug_mask & DEBUG_USER_STATE) {
219                 struct timespec ts;
220                 struct rtc_time tm;
221                 getnstimeofday(&ts);
222                 rtc_time_to_tm(ts.tv_sec, &tm);
223                 pr_info("request_suspend_state: %s (%d->%d) at %lld "
224                         "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
225                         new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
226                         prev_state, new_state,
227                         ktime_to_ns(ktime_get()),
228                         tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
229                         tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
230         }
231         if (!old_sleep && new_state != PM_SUSPEND_ON) {
232                 state |= SUSPEND_REQUESTED;
233                 queue_up_early_suspend_work(&early_suspend_work);
234         } else if (old_sleep && new_state == PM_SUSPEND_ON) {
235                 state &= ~SUSPEND_REQUESTED;
236                 __pm_stay_awake(early_suspend_ws);
237                 queue_up_early_suspend_work(&late_resume_work);
238         }
239         suspend_state = new_state;
240         spin_unlock_irqrestore(&state_lock, irqflags);
241         mutex_unlock(&suspend_lock);
242 }

这个函数主要就是区分是要走earlysuspend 还是lateresume,然后调用&early_suspend_work)这个工作队列,其实就是调用early_suspend函数
136 static void early_suspend(struct work_struct *work)
137 {
138         struct early_suspend *pos;
139         unsigned long irqflags;
140         int abort = 0;
141
142         mutex_lock(&early_suspend_lock);
143         spin_lock_irqsave(&state_lock, irqflags);
144         if (state == SUSPEND_REQUESTED)
145                 state |= SUSPENDED;
146         else
147                 abort = 1;
148         spin_unlock_irqrestore(&state_lock, irqflags);
149
150         if (abort) {
151                 if (debug_mask & DEBUG_SUSPEND)
152                         pr_info("early_suspend: abort, state %d\n", state);
153                 mutex_unlock(&early_suspend_lock);
154                 goto abort;
155         }
156
157         if (debug_mask & DEBUG_SUSPEND)
158                 pr_info("early_suspend: call handlers\n");
159         list_for_each_entry(pos, &early_suspend_handlers, link) {
160                 if (pos->suspend != NULL)
161                         pos->suspend(pos);
162         }
163         mutex_unlock(&early_suspend_lock);
164
165         if (debug_mask & DEBUG_SUSPEND)
166                 pr_info("early_suspend: after call handlers\n");
167         /* just wake up flusher to start write back and don't wait it finished*/
168         wakeup_flusher_threads(0, WB_REASON_SYNC);
169 abort:
170         spin_lock_irqsave(&state_lock, irqflags);
171         if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
172                 __pm_relax(early_suspend_ws);
173         spin_unlock_irqrestore(&state_lock, irqflags);
174         mod_timer(&monitor_timer, jiffies + monitor_period*HZ);
175         queue_work(suspend_wq, &suspend_work);
176 }

这里就是逐个调用平台注册过得earlysuspend函数,按照leave来调用哦,注册时候会定义,然后系统会调用 queue_work(suspend_wq, &suspend_work);这个工作队列,这个其实就是try_to_suspend,

68 static void try_to_suspend(struct work_struct *work)
 69 {
 70         unsigned int initial_count, final_count;
 71
 72         if (!pm_get_wakeup_count(&initial_count, true) ||
 73             !alarm_pm_wake_check())
 74                 goto queue_again;
 75
 76         mutex_lock(&suspend_lock);
 77
 78         if (!pm_save_wakeup_count(initial_count)) {
 79                 mutex_unlock(&suspend_lock);
 80                 goto queue_again;
 81         }
 82         del_timer_sync(&monitor_timer);
 83
 84         if (suspend_state == PM_SUSPEND_ON) {
 85                 mutex_unlock(&suspend_lock);
 86                 return;
 87         }
 88
 89         if (suspend_state >= PM_SUSPEND_MAX)
 90                 hibernate();
 91         else
 92                 pm_suspend(suspend_state);
 93
 94         mutex_unlock(&suspend_lock);
 95
 96         if (!pm_get_wakeup_count(&final_count, false))    
 97                 goto queue_again;
 98
 99         /*
100          * If the wakeup occured for an unknown reason, wait to prevent the
101          * system from trying to suspend and waking up in a tight loop.
102          */
103         if (final_count == initial_count)
104                 schedule_timeout_uninterruptible(HZ / 2);
105
106 queue_again:
107         queue_work(suspend_wq, &suspend_work);
108 }

函数嘴开始就执行了 pm_get_wakeup_count()这个函数,这个函数:

722 bool pm_get_wakeup_count(unsigned int *count, bool block)
723 {
724         unsigned int cnt, inpr;
725
726         if (block) {
727                 DEFINE_WAIT(wait);
728
729                 for (;;) {
730                         prepare_to_wait(&wakeup_count_wait_queue, &wait,
731                                         TASK_INTERRUPTIBLE);
732                         split_counters(&cnt, &inpr);
733                         if (inpr == 0 || signal_pending(current))
734                                 break;
735
736                         schedule();
737                 }
738                 finish_wait(&wakeup_count_wait_queue, &wait);
739         }
740
741         split_counters(&cnt, &inpr);
742         *count = cnt;
743         return !inpr;
744 }

看到了刚才我们介绍unwake_lock函数的时候碰见的wakeup_count_wait_queue等待队列了吧,没错,这里就循环等待是不是有应用或者driver拿着锁,如果拿着就调度出去做别的事情,等待unwake_lock的显示唤醒队列,然后再去检查split_counters(&cnt, &inpr);锁数量,如果没有锁了,OK,我们跳出循环,准备进入下面的suspend流程,到这里是不是就算解释清楚,wakelock和suspend的关系了呢?上层 中层 底层调用基本上已经联系一起了,我会在下篇文章继续分析suspend的流程!

还是一样,本人小菜鸟一名,如果不对,请指出,谢谢!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
author:杨兴达; 邮箱:[email protected] 电话:180 1018 0585 Android 电源管理 -- wakelock机制,通过控制wakelock 实现保持pad 禁止休眠状态; Wake Lock是一种锁的机制, 只要有人拿着这个锁,系统就无法进入休眠, 可以被用户态程序和内核获得. 这个锁可以是有超时的 或者 是没有超时的, 超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时了, 内核就会启动休眠的那套机制来进入休眠. PowerManager.WakeLock 有加锁和解锁两种状态,加锁的方式有两种: 第一种是永久的锁住,这样的锁除非显式的放开,否则是不会解锁的,所以这种锁用起来要非常的小心。 第二种锁是超时锁,这种锁会在锁住后一段时间解锁。 在创建了 PowerManager.WakeLock 后,有两种机制,第一种是不计数锁机制,另一种是计数锁机制。可以通过 setReferenceCounted(boolean value) 来指定,一般默认为计数机制。这两种机制的区别在于,前者无论 acquire() 了多少次,只要通过一次 release()即可解锁。而后者正真解锁是在( --count == 0 )的时候,同样当 (count == 0) 的时候才会去申请加锁。所以 PowerManager.WakeLock 的计数机制并不是正真意义上的对每次请求进行申请/释放每一把锁,它只是对同一把锁被申请/释放的次数进行了统计,然后再去操作。 源码 位置:frameworks/base/core/java/android/os/PowerManager.java ++++++++++++++++++++++++ 讲述 应用层 申请的锁 怎么传到kernel下面的,来理解 整个wakelock的框架。 比如android跑起来之后 在 /sys/power/wake_lock 下面的PowerManagerService 的生成过程。 1).应用程序申请锁 Android 提供了现成 android.os.PowerManager 类 , 类中 提供newWakeLock(int flags, String tag)方法 来取得 应用层的锁, 此函数的定义 frameworks/base/core/java/android/os/PowerManager.java 应用程序 在申请wake_lock时 都会有调用以下 部分。 实例: PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, “MyTag”); wl.acquire();//申请锁,这里会调用PowerManagerService里面acquireWakeLock() *********************** wl.release(); //释放锁,显示的释放锁,如果申请的锁不在此释放,系统就不会进入休眠。 ====================================== 2). frameworks层 /frameworks/base/services/java/com/android/server/PowerManagerService.java这个类是来管理 所有的应用程序 申请的wakelock。比如音视、频播放器、camera等申请的wakelock 都是通过这个类来 管理的。 static final String PARTIAL_NAME ="PowerManagerService" nativeAcquireWakeLock(PARTIAL_WAKE_LOCK_ID, PARTIAL_NAME); 上面 这个函数调用Power类 里面的 acquireWakeLock(),此时的PARTIAL_NAME作为参数传递到底层去。 public static native void nativeAcquireWakeLock(int lock, String id); 注:在PowerManagerService 类中没有实现nativeAcquireWakeLock,其实现体在 frameworks/base/core/jni/android_os_Power.cpp中,所以nativeAcquireWakeLock()方法时会调用JNI下的实现方法。 3).JNI层的实现 路径:frameworks/base/core/jni/android_os_Power.cpp // static void acquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj) { ************** const char *id = env->GetStringUTFChars(idObj, NULL); acquire_wake_lock(lock, id); env->ReleaseStringUTFChars(idObj, id); } 注:在acquireWakeLock()中调用了 路径下hardware/libhardware_legacy/power/power.c下面的acquire_wake_lock(lock, id) 4).与kernel层的交互 在power.c下的acquire_wake_lock(lock, id)函数如下: int acquire_wake_lock(int lock, const char* id) { ************** return write(fd, id, strlen(id)); } 注: fd就是文件描述符,在此 表示”/sys/power/wake_lock” id就是从PowerManagerService类中传下来的参数即:PARTIAL_NAME = "PowerManagerService" 到此 就是通过 文件系统 来与kernel层 交互的地方。 +++++++++++++++++++++++++++++++++++++++++++++++++++++ PowerManager类被应用程序调用,控制电源设备状态切换: PowerManager类对外有三个接口函数: 1、void goToSleep(long time); //强制设备进入Sleep状态 Note:在应用层调用该函数, 应用需要 在 源码下编译,用系统签名,否则 调用 此函数 出错; 2、newWakeLock(int flags, String tag);//取得相应层次的锁 flags参数说明: PARTIAL_WAKE_LOCK :保持CPU 运转,屏幕和键盘灯是关闭的。 SCREEN_DIM_WAKE_LOCK :保持CPU 运转,允许保持屏幕显示但有可能是灰的,关闭键盘灯 SCREEN_BRIGHT_WAKE_LOCK :保持CPU 运转,保持屏幕高亮显示,关闭键盘灯 FULL_WAKE_LOCK :保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度 ACQUIRE_CAUSES_WAKEUP: 一旦有请求锁时,强制打开Screen和keyboard light ON_AFTER_RELEASE: 在释放锁时reset activity timer Note: 如果申请了partial wakelock,那么即使按Power键,系统也不会进Sleep,如Music播放时 如果申请了其它的wakelocks,按Power键,系统还是会进Sleep 3、void userActivity(long when, boolean noChangeLights);//User activity事件发生,设备会被切换到Full on的状态,同时Reset Screen off timer. PowerManager和WakeLock的操作步骤 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);通过 Context.getSystemService().方法获取PowerManager实例。 然后通过PowerManager的newWakeLock ((int flags, String tag)来生成WakeLock实例。int Flags指示要获取哪种WakeLock,不同的Lock对cpu 、屏幕、键盘灯有不同影响。 获取WakeLock实例后通过acquire()获取相应的锁,然后进行其他操作,最后使用release()释放(释放是必须的)。 Note: 1. 在使用以上函数的应用程序中,必须在其Manifest.xml文件中加入下面的权限: <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DEVICE_POWER" /> 2. 所有的锁必须成对的使用, 如果申请了而没有及时释放,会造成系统故障。如申请了partial wakelock,而没有及时释放, 那系统就永远进不了Sleep模式.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值