您的位置:首页 > 移动开发 > Android开发

Android kernel wakelock分析

2013-11-04 14:25 387 查看
1. Linux kernel wakelock 定义

A wake_lock prevents the system from entering suspend or other low power states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock prevents a full system suspend. If the type is WAKE_LOCK_IDLE, low power

states that cause large interrupt latencies or that disable a set of interrupts will not entered from idle until the wake_locks are released.

当唤醒锁被激活的时候,它能阻止系统进入挂起状态或者其他低功耗模式。

如果锁类型被设置为 WAKE_LOCK_SUSPEND,该锁阻止整个系统进入挂起状态。

如果锁类型被设置为 WAKE_LOCK_IDLE, 除非该锁被释放,产生中断大延迟或者失能中断集的系统低功耗模式将被阻止进入。但是系统可以进入挂起睡眠状态。

2. 编译标志

根据Kconfig 文件, 相关的内核编译标志分别为:

CONFIG_HAS_WAKELOCK 选择是否使能 wakelock模块。

CONFIG_WAKELOCK: 依赖CONFIG_HAS_WAKELOCK。当用户空间请求睡眠状态时,该请求将被延置直到没有唤醒锁为使能状态。

CONFIG_WAKELOCK_STAT: 报告唤醒锁状态并写入到 /proc/wakelocks 文件

CONFIG_USER_WAKELOCK 用户空间唤醒锁。写入"锁名称" 或者“超时锁名称” 到 /sys/power/wake_lock文件来使能唤醒锁 ( 根据需要与否来创建一个唤醒锁)

写入“锁名称”到 /sys/power/wake_unlock 文件来解锁一个用户空间唤醒锁。

CONFIG_EARLYSUSPEND 当用户空间请求睡眠状态改变时, 调用early suspend handlers

该标志依赖于 CONFIG_WAKELOCK编译标志。

3. /proc/wakelocks 格式

示例如下

root@android:/ # cat /proc/wakelocks

cat /proc/wakelocks

name count expire_count wake_count active_since total_time sleep_time max_time last_change

"em" 4899 0 0 0 129695047374 123649908727 2367225591 29445791758668

"alarm_rtc" 2360 474 0 0 1613420206524 1611455462922 1949640000 27899269419825

"bcmpmu_i2c" 244500 0 1876 0 275160569412 268463363255 996054076 29445791728151

"KeyEvents" 11133 0 0 0 1853662043 601848424 231933595 29439508921751

4. 内核锁函数

初始化锁模块

[cpp] view plaincopyprint?

static int __init wakelocks_init(void)

{

int ret;

int i;



// 初始化suspend, idle 两个类型锁的链表

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

INIT_LIST_HEAD(&active_wake_locks[i]);



#ifdef CONFIG_WAKELOCK_STAT

wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,

"deleted_wake_locks");

#endif

       //初始化 main 锁并上锁。 何处解main锁?early_suspend()

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

wake_lock(&main_wake_lock);

       //初始化 unknown_wakeups 锁

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

       //初始化 suspend_backoff 锁

       //以上几个都是suspend 锁。 用户空间锁也是 suspend 类型。

wake_lock_init(&suspend_backoff_lock, WAKE_LOCK_SUSPEND,

"suspend_backoff");



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;

}



suspend_work_queue = create_singlethread_workqueue("suspend");

if (suspend_work_queue == NULL) {

ret = -ENOMEM;

goto err_suspend_work_queue;

}



#ifdef CONFIG_WAKELOCK_STAT

       //创建 /proc/wakelocks,  /proc/active_wakelocks 节点输出锁状态信息

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

proc_create("active_wakelocks", S_IRUGO, NULL, &active_wakelocks_fops);

#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(&suspend_backoff_lock);

wake_lock_destroy(&unknown_wakeup);

wake_lock_destroy(&main_wake_lock);

#ifdef CONFIG_WAKELOCK_STAT

wake_lock_destroy(&deleted_wake_locks);

#endif

return ret;

}

初始化锁

[cpp] view plaincopyprint?

void wake_lock_init(struct wake_lock *lock, int type, const char *name)

wake_lock_init() 设置锁的名字,类型,最后将新建的锁挂接到一个专门链接这些非锁状态的链表inactive_locks上。

上锁函数

[cpp] view plaincopyprint?

void wake_lock(struct wake_lock *lock)

{

wake_lock_internal(lock, 0, 0);

}

EXPORT_SYMBOL(wake_lock);



void wake_lock_timeout(struct wake_lock *lock, long timeout)

{

wake_lock_internal(lock, timeout, 1);

}

wake_lock()和wake_lock_timeout() 分别对 inactive 的锁进行上锁。后者是超时锁。调用同一个内部函数 wake_lock_internal()

[cpp] view plaincopyprint?

static void wake_lock_internal(struct wake_lock *lock, long timeout, int has_timeout)

{

spin_lock_irqsave(&list_lock, irqflags);

type = lock->flags & WAKE_LOCK_TYPE_MASK;



       //上锁前, 检查锁类型和有效性

BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);

BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));

#ifdef CONFIG_WAKELOCK_STAT

       //检查 wait_for_wakeup 标志

if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {

if (debug_mask & DEBUG_WAKEUP)

pr_info("wakeup wake lock: %s\n", lock->name);

wait_for_wakeup = 0;

lock->stat.wakeup_count++;

}

       //检查超时锁

if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&

(long)(lock->expires - jiffies) <= 0) {

wake_unlock_stat_locked(lock, 0);

lock->stat.last_time = ktime_get();

}

#endif

       //上锁

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

lock->flags |= WAKE_LOCK_ACTIVE;

#ifdef CONFIG_WAKELOCK_STAT

lock->stat.last_time = ktime_get();

#endif

}

       //先从链表中摘除该锁

list_del(&lock->link);

if (has_timeout) {

if (debug_mask & DEBUG_WAKE_LOCK)

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

} else {

if (debug_mask & DEBUG_WAKE_LOCK)

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

}

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)

                     //检查并遍历更新队列中所有超时锁。

expire_in = has_wake_lock_locked(type);

else

                     //非超时锁

expire_in = -1;

if (expire_in > 0) {

if (debug_mask & DEBUG_EXPIRE)

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

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

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

                     //如果超时锁为0, 且无非超时锁时, expire_in为0, 启动suspend流程

if (expire_in == 0)

queue_work(suspend_work_queue, &suspend_work);

}

}

spin_unlock_irqrestore(&list_lock, irqflags);

}

解锁函数

[cpp] 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\n", lock->name);

       //去处锁标志

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

       //该锁从 active列表移除, 加入inactive列表

list_del(&lock->link);

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

if (type == WAKE_LOCK_SUSPEND) {

              //检查是否需要启动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\n", 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\n", 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);

}

expire_wake_locks() 检查是否所有锁已经超时。 如果has_wake_lock_locked() 返回0,

针对suspend锁,必然是超时锁为0且非超时锁为0, 则启动suspend流程。

expire_wake_locks()是 expire_timer 的超时回调函数。

[cpp] view plaincopyprint?

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

5. suspend函数

[cpp] view plaincopyprint?

static void suspend(struct work_struct *work)

{

int ret;

int entry_event_num;

struct timespec ts_entry, ts_exit;



//如果还有suspend锁,直接abort

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

getnstimeofday(&ts_entry);

       //系统suspend

       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)\n", 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\n");

wake_lock_timeout(&unknown_wakeup, HZ / 2);

}

}

static DECLARE_WORK(suspend_work, suspend);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: