三、kernel层源码解析 - wakelock的重要地位

wakelock在android的休眠唤醒机制中扮演着及其重要的角色,主要源码位于文件:kernel/kernel/power/wakelock.c,kernel/include/linux/wakelock.h中。

wakelocks_init()函数所做的工作是整个wakelock可以工作起来的基础,所有这里先说说这个函数。

static int __init wakelocks_init(void)

{

int ret;

int i;

for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)

INIT_LIST_HEAD(&active_wake_locks[i]);

// 初始化active_wake_locks数组中的两个类型锁链表: WAKE_LOCK_SUSPEND,WAKE_LOCK_IDLE

#ifdef CONFIG_WAKELOCK_STAT // defined

wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,

"deleted_wake_locks");

// 初始化wakelock deleted_wake_locks,同时将其加入到非活动锁链表中

#endif

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

wake_lock_init(&sys_sync_wake_lock, WAKE_LOCK_SUSPEND, "sys_sync");

wake_lock(&main_wake_lock);

wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");

// 初始化wakelock: main, sys_sync, unknown_wakeups, 同时将其加入到非活动锁链表中

// 给 main_wake_lock 加锁

ret = platform_device_register(&power_device);

if (ret) {

pr_err("[wakelocks_init]: platform_device_register failed/n");

goto err_platform_device_register;

}

ret = platform_driver_register(&power_driver);

if (ret) {

pr_err("[wakelocks_init]: platform_driver_register failed/n");

goto err_platform_driver_register;

}

// 新建工作队列和工作者内核线程: sys_sync_work_queue, fs_sync

// suspend_work_queue, suspend

sys_sync_work_queue = create_singlethread_workqueue("fs_sync");

if (sys_sync_work_queue == NULL) {

pr_err("[wakelocks_init] fs_sync workqueue create failed/n");

}

suspend_work_queue = create_singlethread_workqueue("suspend");

if (suspend_work_queue == NULL) {

ret = -ENOMEM;

goto err_suspend_work_queue;

}

#ifdef CONFIG_WAKELOCK_STAT

proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);

// 创建proc接口

#endif

return 0;

err_suspend_work_queue:

platform_driver_unregister(&power_driver);

err_platform_driver_register:

platform_device_unregister(&power_device);

err_platform_device_register:

wake_lock_destroy(&unknown_wakeup);

wake_lock_destroy(&main_wake_lock);

#ifdef CONFIG_WAKELOCK_STAT

wake_lock_destroy(&deleted_wake_locks);

#endif

return ret;

}

可以看到该初始化函数中新建了几个wakelock: deleted_wake_locks、main_wake_lock、sys_sync_wake_lock、unknown_wakeup,他们全部都是WAKE_LOCK_SUSPEND类型的wakelock,说到这里不得不提到wakelock的两种类型了:

1. WAKE_LOCK_SUSPEND – 这种锁如果被某个task持有,那么系统将无法进入休眠。

2. WAKE_LOCK_IDLE – 这种锁不会影响到系统进入休眠,但是如果这种锁被持有,那么系统将无法进入idle空闲模式。

不过常用的所类型还是WAKE_LOCK_SUSPEND,包括userwakelock.c提供给用户空间的新建wakelock的接口,都是建立的第一种锁。另外系统为了分开管理这两种不同类型的锁,建立了两个链表来统一链接不同类型的锁:active_wake_locks[],这个是具有两个链表头的数组,元素0是挂接WAKE_LOCK_SUSPEND类型的锁,而元素1就是挂接WAKE_LOCK_IDLE类型的wakelock了。

接着上面说,这个初始化函数新建这些锁之后,直接将主锁(main_wake_lock)给上锁了,其余都是非锁状态。新建wakelock使用函数wake_lock_init(),该函数设置锁的名字,类型,最后将新建的锁挂接到一个专门链接这些非锁状态的链表inactive_locks上(新建的wakelock初期都是出于非锁状态的,除非显示调用函数wake_lock来上锁)。接着如果使用函数wake_lock()来给特定的wakelock上锁的话,会将该锁从链表inactive_locks上移动到对应类型的专用链表上active_wake_locks[type]上。

wakelock有两种形式的锁:超时锁和非超时锁,这两种形式的锁都是使用函数wake_lock_init()来初始化,只是在上锁的时候会有一点点差别,超时锁使用函数wake_lock_timeout(),而非超时锁使用函数wake_lock(), 这个两个函数会最终调用到同一个函数wake_lock_internal(),该函数依靠传入的不同参数来选择不同的路径来工作。值得注意的是,非超时锁必须手工解锁,否则系统永远不能进入睡眠。下面是wake_lock_internal()函数的片段:

if (!(lock->flags & WAKE_LOCK_ACTIVE))

lock->flags |= WAKE_LOCK_ACTIVE;// wakelock状态为inactive,则更改为active

if (has_timeout) { // wake_lock_timeout()会传入1

if (wakelock_debug_mask & DEBUG_WAKE_LOCK)

pr_info("[wake_lock_internal]: %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]);

}

// acquire a non-timeout wakelock 添加一个非超时锁

else { // wake_lock ()会传入0

if (wakelock_debug_mask & DEBUG_WAKE_LOCK)

pr_info("[wake_lock_internal]: %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]);

// 将刚刚设置的非超时锁加到对应类型的活动锁链表中

}

解锁的时候,这两种形式的锁所使用函数都是一样了:wake_unlock(),该函数中会首先作如下操作:

lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);

// 清除锁活动标志和自动超时标志

list_del(&lock->link); // 从锁对应的活动链表上摘除

list_add(&lock->link, &inactive_locks);

// 将unlock的锁挂接到非活动链表inactive_locks上

前面已经说了只有类型为WAKE_LOCK_SUSPEND的wakelock被上锁才会阻止系统进入suspend,那么也就是说只要链表active_wake_locks[WAKE_LOCK_SUSPEND]为NULL,那么系统就可以执行suspend的流程了。Android对linux的改造,让其可以在三种情况下进入linux的标准suspend的流程:

1. wake_unlock(),这个应该是最容易想到的,只要系统有对WAKE_LOCK_SUSPEND类型的wakelock解锁的动作,都有可能会进入suspend流程开始休眠,为什么是有可能呢?因为可能还有超时锁没有被超时解锁。下面看一下代码片段:

void wake_unlock(struct wake_lock *lock)

{

if (type == WAKE_LOCK_SUSPEND) // 貌似只在处理这个类型的wakelock

{

long has_lock = has_wake_lock_locked(type);

// 这个函数蛮重要,它来检查type类型的链表上是否还有锁被上锁了。

// 其返回值如果是0,说明没有该类型的锁被持有了;返回非0表明就是这个类型的活动链表上还存在超时锁但是没有非超时锁了,这个返回值就是当前时间距离最后超时的锁超时时间的jiffies值;如果返回-1,那表明还有该类型的非超时锁被持有。

if (wakelock_debug_mask & DEBUG_WAKE_LOCK)

pr_info("[wake_unlock]: has_lock = 0x%x/n" , has_lock);

if (has_lock > 0) {

if (wakelock_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);

// 修改定时器的超时值并add该定时器

}

else // 已经没有超时锁了

{

if (del_timer(&expire_timer)) // 删除定时器

if (wakelock_debug_mask & DEBUG_EXPIRE)

pr_info("[wake_unlock]: %s, stop expire "

"timer/n", lock->name);

if (has_lock == 0)

// !=0,表明还有该类型的非超时锁被持有,现在还不能进入suspend

{

pr_info("[wake_unlock]: (%s) suspend_work_queue suspend_work/n" , lock->name);

queue_work(suspend_work_queue, &suspend_work);

// 提交suspend的工作项,开始执行标准linux的suspend流程

}

}

}

spin_unlock_irqrestore(&list_lock, irqflags);

}

2. 超时锁超时之后,定时器的回调函数会执行会查看是否有其他的wakelock, 如果没有, 就在这里让系统进入睡眠。

static void expire_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)

// 如果没有SUSPEND类型的wakelock处于active,那么将调用suspend

queue_work(suspend_work_queue, &suspend_work);

spin_unlock_irqrestore(&list_lock, irqflags);

}

static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);

列出以下一个重要的函数源码:

static long has_wake_lock_locked(int type)

{

struct wake_lock *lock, *n;

long max_timeout = 0;

BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);

list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {

if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {

long timeout = lock->expires - jiffies;

if (timeout <= 0)

expire_wake_lock(lock);

else if (timeout > max_timeout)

max_timeout = timeout;

} else

return -1;

}

return max_timeout;

}

3. 这个可能有人觉得匪夷所思,就是在wake_lock{_ _timeout}()函数中,调用了内部函数wake_lock_internal()。这里只有在对超时锁上锁的时候才有可能进入休眠,如果对一个费超时锁上锁的话,那么就没有必要去检查活动链表了。

static void wake_lock_internal(

struct wake_lock *lock, long timeout, int has_timeout)

{

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) // 超时锁的时候传进来的是1

expire_in = has_wake_lock_locked(type);

// 检查当前锁类型链表上是否还有锁处于active的状态,无返回0

else

expire_in = -1;

// 如果是非超时锁的话,这里直接赋值-1,省去了活动链表检查步骤了

if (expire_in > 0) {

if (debug_mask & DEBUG_EXPIRE)

pr_info("wake_lock: %s, start expire timer, "

"%ld/n", lock->name, expire_in);

// modify the time wakelock is expired

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) // 没有锁处于active状态后,准备调用suspend了

{

pr_info("[wake_lock]: suspend_work_queue suspend_work/n ");

queue_work(suspend_work_queue, &suspend_work);

}

}

}

spin_unlock_irqrestore(&list_lock, irqflags);

}

下面是suspend的工作项,经过上面三种情况的检查,ok之后将会提交该工作项给工作队列suspend_work_queue,如下:

static void suspend(struct work_struct *work)

{

int ret;

int entry_event_num;

// there are still some wakelock

if (has_wake_lock(WAKE_LOCK_SUSPEND)) {

if (wakelock_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);

// requested_suspend_state这个全局变量在函数request_suspend_state()中被设置,也就是执行了eraly suspend或者late resume之后,主要是为suspend保留请求的省电状态。

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: exit suspend, 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_suspend returned with no event/n");

wake_lock_timeout(&unknown_wakeup, HZ / 2);

}

}

static DECLARE_WORK(suspend_work, suspend);

@kernel/kernel/power/suspend.c

int pm_suspend(suspend_state_t state)

{

if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)

return enter_state(state);

// 标准linux的suspend流程函数

return -EINVAL;

}

EXPORT_SYMBOL(pm_suspend);

Wakelock的机制被文件userwakelock.c中的code封装成了sys的接口sys/power/wake_lock和sys/power/wake_unlock文件,那么上层如果需要新建wakelock或者注销wakelock,或者是解锁wakelock,都是操作这两个sys接口文件。

更多相关文章

  1. C语言函数的递归(上)
  2. Android(安卓)5.1 SurfaceFlinger VSYNC详解
  3. Android的MediaPlayer
  4. Linux Kernel and Android(安卓)休眠与唤醒
  5. Android网络状态实时监听实例代码(二)
  6. Android(安卓)JNI开发入门之二
  7. Android培训班(9)
  8. 【android】喜马拉雅FM sdk使用
  9. linux kernel suspend Resume

随机推荐

  1. Python——域名解析成IP地址
  2. 使用python实现一个简单的学生信息管理系
  3. 饮冰三年-人工智能-Python-22 Python初识
  4. 使用Python操作Redis
  5. 更改Ubuntu默认python版本
  6. Python虚拟环境包导出
  7. Python - 将值打印到新文件?
  8. 真正最正确的用BAT运行JAVA不显示DOS窗口
  9. Tkinter小部件上的垂直和水平滚动条
  10. 在Python中TypeError: object() takes no