Android kernel wakelock分析

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值