目录简介一、wakelocks1、Kernel wakelocks在电源管理中的位置二、wakelocks 内核源码分析1、创建 /sys/power/wake_lock 和 /sys/power/wake_unlock2、pm_wake_lock() 接口3、pm_wake_unlock() 接口4、__wakelocks_gc()回收处理work三、工作时序简介Wakelocks 框架是基于Wakeup Source实现的为Android系统上层提供投票机制以阻止系统进入休眠。该模块的支持受宏CONFIG_PM_WAKELOCKS控制。在使能该宏的情况下PM Core初始化过程中会在sysfs下创建两个属性节点/sys/power/wake_lock:用户程序可以向其写入一个字符串来创建一个wakelock该字符创即为wakelock的名字该wakelock可阻止系统进入低功耗模式/sys/power/wake_unlock:用户程序向其写入相同的字符串即可注销该wakelock配置宏CONFIG_PM_WAKELOCKS_LIMIT可以限制系统所能创建的wakelock的数量。使能宏CONFIG_PM_WAKELOCKS_GC能打开wakelock的回收机制使得wakelock在积累一定的数量后再去清除释放空间从而不需要在每次释放wakelock时都去清除。一、wakelockswakelocks提供的功能包括1/sys/power/wake_lock用户程序向文件写入一个字符串即可创建一个wakelock该字符串就是wakelock的名字。该wakelock可以阻止系统进入低功耗模式2/sys/power/wake_unlock用户程序向文件写入相同的字符串即可注销一个wakelock3当系统中所有的wakelock都注销后系统可以自动进入低功耗状态由autosleep实现4向内核其它driver也提供了wakelock的创建和注销接口允许driver创建wakelock以阻止睡眠、注销wakelock以允许睡眠。并允许用户空间访问有关Android wakelocks更为详细的描述可以参考http://elinux.org/Android_Power_Management1、Kernel wakelocks在电源管理中的位置在PM core中有一个wakelock模块kernel/power/wakelock.c该模块依赖wakeup events framework提供的wakeup source机制实现用户空间的wakeup source就是wakelocks并通过PM core main模块向用户空间提供两个同名的sysfs文件wake_lock和wake_unlock。二、wakelocks 内核源码分析该模块的支持受宏CONFIG_PM_WAKELOCKS控制。在使能该宏的情况下PM Core初始化过程中会在sysfs下创建两个属性节点/sys/power/wake_lock用户程序可以向其写入一个字符串来创建一个wakelock该字符创即为wakelock的名字该wakelock可阻止系统进入低功耗模式/sys/power/wake_unlock用户程序向其写入相同的字符串即可注销该wakelock1、创建 /sys/power/wake_lock 和 /sys/power/wake_unlockpm_init() 调用 kobject_create_and_add 和 sysfs_create_groups 函数创建一系列的属性文件其中 wake_lock_attr、wake_unlock_attr 分别会生成 /sys/power/wake_lock 和 /sys/power/wake_unlock/* kernel/power/main.c */ 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_SUSPEND mem_sleep_attr.attr, sync_on_suspend_attr.attr, #endif #ifdef CONFIG_PM_AUTOSLEEP autosleep_attr.attr, #endif #ifdef CONFIG_PM_WAKELOCKS wake_lock_attr.attr, //wake_lock wake_unlock_attr.attr, //wake_unlock #endif #ifdef CONFIG_PM_SLEEP_DEBUG pm_test_attr.attr, pm_print_times_attr.attr, pm_wakeup_irq_attr.attr, pm_debug_messages_attr.attr, #endif #endif #ifdef CONFIG_FREEZER pm_freeze_timeout_attr.attr, #endif NULL, }; static const struct attribute_group attr_group { .attrs g, }; static const struct attribute_group *attr_groups[] { attr_group, #ifdef CONFIG_PM_SLEEP suspend_attr_group, #endif NULL, }; struct workqueue_struct *pm_wq; EXPORT_SYMBOL_GPL(pm_wq); static int __init pm_start_workqueue(void) { pm_wq alloc_workqueue(pm, WQ_FREEZABLE, 0); return pm_wq ? 0 : -ENOMEM; } static int __init pm_init(void) { int error pm_start_workqueue(); if (error) return error; hibernate_image_size_init(); hibernate_reserved_size_init(); pm_states_init(); power_kobj kobject_create_and_add(power, NULL); if (!power_kobj) return -ENOMEM; error sysfs_create_groups(power_kobj, attr_groups); if (error) return error; pm_print_times_init(); return pm_autosleep_init(); } core_initcall(pm_init);wake_lock_attr 和 wake_unlock_attr 的 .show、.store 定义如下#define power_attr(_name) \ static struct kobj_attribute _name##_attr { \ .attr { \ .name __stringify(_name), \ .mode 0644, \ }, \ .show _name##_show, \ .store _name##_store, \ } #ifdef CONFIG_PM_WAKELOCKS static ssize_t wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return pm_show_wakelocks(buf, true); } static ssize_t wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { int error pm_wake_lock(buf); return error ? error : n; } power_attr(wake_lock); static ssize_t wake_unlock_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return pm_show_wakelocks(buf, false); } static ssize_t wake_unlock_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { int error pm_wake_unlock(buf); return error ? error : n; } power_attr(wake_unlock); #endif /* CONFIG_PM_WAKELOCKS */用户空间往/sys/power/wake_lock写数据最终调用 pm_wake_lock用户空间往/sys/power/wake_unlock写数据最终调用 pm_wake_unlock2、pm_wake_lock() 接口向/sys/power/wake_lock写如字符串时调用pm_wake_lock()pm_wake_lock()主要实现功能查找同名 wakelock找不到时创建 wakelock并持超时锁配置CONFIG_PM_WAKELOCKS_LIMIT 0的情况下对wakelock数量计数并限制将该wakelock移到回收链表前端以防被优先回收/* kernel/power/wakelock.c*/ int pm_wake_lock(const char *buf) { const char *str buf; struct wakelock *wl; u64 timeout_ns 0; size_t len; int ret 0; if (!capable(CAP_BLOCK_SUSPEND)) return -EPERM; //解析传入的字符串第一个参数为wakelock名称第二个参数可选则是wakelock超时时间 while (*str !isspace(*str)) str; len str - buf; if (!len) return -EINVAL; if (*str *str ! \n) { /* Find out if theres a valid timeout string appended. */ ret kstrtou64(skip_spaces(str), 10, timeout_ns); if (ret) return -EINVAL; } mutex_lock(wakelocks_lock); //查找wakelock找不到时创建 wl wakelock_lookup_add(buf, len, true); if (IS_ERR(wl)) { ret PTR_ERR(wl); goto out; } if (timeout_ns) { //如果传入了超时参数则持锁超时后会自动释放该锁 u64 timeout_ms timeout_ns NSEC_PER_MSEC - 1; do_div(timeout_ms, NSEC_PER_MSEC); __pm_wakeup_event(wl-ws, timeout_ms); } else { //否则直接持锁 __pm_stay_awake(wl-ws); } wakelocks_lru_most_recent(wl); //将该wakelock移到回收链表前端使得回收机制触发时靠后处理 out: mutex_unlock(wakelocks_lock); return ret; }wakelock_lookup_add() 查找wakelock找不到时创建static struct wakelock *wakelock_lookup_add(const char *name, size_t len, bool add_if_not_found) { struct rb_node **node wakelocks_tree.rb_node; struct rb_node *parent *node; struct wakelock *wl; //根据名称在红黑树上查找是否已经存在该wakelock while (*node) { int diff; parent *node; wl rb_entry(*node, struct wakelock, node); diff strncmp(name, wl-name, len); if (diff 0) { if (wl-name[len]) diff -1; else return wl; //找到同名wakelock返回 } if (diff 0) node (*node)-rb_left; else node (*node)-rb_right; } if (!add_if_not_found) return ERR_PTR(-EINVAL); //配置CONFIG_PM_WAKELOCKS_LIMIT0的情况下会检测已创建的wakelock数量是否已经超过该配置 if (wakelocks_limit_exceeded()) return ERR_PTR(-ENOSPC); //未找到同名wakelock的情况下开始创建wakelock /* Not found, we have to add a new one. */ wl kzalloc(sizeof(*wl), GFP_KERNEL); if (!wl) return ERR_PTR(-ENOMEM); wl-name kstrndup(name, len, GFP_KERNEL); if (!wl-name) { kfree(wl); return ERR_PTR(-ENOMEM); } //本质wakelock是通过wakeup_source机制实现的 wl-ws wakeup_source_register(NULL, wl-name); if (!wl-ws) { kfree(wl-name); kfree(wl); return ERR_PTR(-ENOMEM); } wl-ws-last_time ktime_get(); //将该wakelock挂到红黑树上 rb_link_node(wl-node, parent, node); rb_insert_color(wl-node, wakelocks_tree); wakelocks_lru_add(wl); //添加到回收链表 increment_wakelocks_number(); //wakelock数量1 return wl; }3、pm_wake_unlock()接口向/sys/power/wake_unlock写入字符串时调用pm_wake_unlock()查找同名wakelock找不到时返回错误释放锁配置CONFIG_PM_WAKELOCKS_GC开启回收机制的情况下对wakelock数量计数并在超过上限时触发回收处理work/* kernel/power/wakelock.c */ int pm_wake_unlock(const char *buf) { struct wakelock *wl; size_t len; int ret 0; if (!capable(CAP_BLOCK_SUSPEND)) return -EPERM; len strlen(buf); if (!len) return -EINVAL; if (buf[len-1] \n) len--; if (!len) return -EINVAL; mutex_lock(wakelocks_lock); //查找wakelock找不到时直接返回错误 wl wakelock_lookup_add(buf, len, false); if (IS_ERR(wl)) { ret PTR_ERR(wl); goto out; } __pm_relax(wl-ws); //释放锁 wakelocks_lru_most_recent(wl); //将该wakelock移到回收链表前端使得回收机制触发时靠后处理 wakelocks_gc(); //已解锁的wakelock加1并判断是否超过上限触发回收处理work out: mutex_unlock(wakelocks_lock); return ret; }4、__wakelocks_gc()回收处理work该接口在已解锁的wakelock数量超过上限 WL_GC_COUNT_MAX(100) 时调用用于处理回收已创建的wakelock释放空间。static void __wakelocks_gc(struct work_struct *work) { struct wakelock *wl, *aux; ktime_t now; mutex_lock(wakelocks_lock); now ktime_get(); //从回收链表尾部开始倒序遍历越靠近链表头部的wakelock越是最近才操作的wakelock list_for_each_entry_safe_reverse(wl, aux, wakelocks_lru_list, lru) { u64 idle_time_ns; bool active; spin_lock_irq(wl-ws-lock); idle_time_ns ktime_to_ns(ktime_sub(now, wl-ws-last_time)); //计算该锁有多长时间未被操作过 active wl-ws-active; //获取锁的激活状态 spin_unlock_irq(wl-ws-lock); if (idle_time_ns ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC)) //如果锁空闲时间小于300s则不再继续回收 break; //如果锁已经失活则注销该锁从红黑树中移除并移除出回收链表释放空间wakelock数量-1 if (!active) { wakeup_source_unregister(wl-ws); rb_erase(wl-node, wakelocks_tree); list_del(wl-lru); kfree(wl-name); kfree(wl); decrement_wakelocks_number(); } } wakelocks_gc_count 0; //重置回收锁计数 mutex_unlock(wakelocks_lock); }回收机制的好处上层频繁操作wakelock时不用每次unlock时都耗时去释放资源如果频繁操作的是同一个wakelock也不用反复创建/释放资源。三、工作时序wakelock的工作时序如下应用程序在处理数据前不希望系统进入休眠状态通过向/sys/power/wake_lock写入一个字符串作为wakelock名字此时pm_wake_lock()被调用在pm_wake_lock()里会查找是否已存在同名wakelock已存在则持锁不存在则创建锁并持锁应用程序在处理完数据后允许系统进入休眠状态时通过向/sys/power/wake_unlock写入已持锁的wakelock名字此时pm_wake_unlock()被调用在pm_wake_unlock()里会查找是否已存在同名wakelock并释放该锁同时判断此时是否要触发wakelock的回收机制当wakelock回收链表里的wakelock数量达到上限后触发wakelock的回收机制将长时间未使用且已经解锁的wakelock注销释放资源把 /sys/power/wake_lock 和 /sys/power/wake_unlock 当成普通文件读写即可参考代码如下static void do_wake_lock(char *module) { char wake_lock_path[] /sys/power/wake_lock; int wake_lock_fd -1; int ret -1; wake_lock_fd open(wake_lock_path, O_WRONLY | O_APPEND); if (wake_lock_fd 0) { printf(%s Failed to open wakelock file - %s, __func__, strerror(errno)); } ret write(wake_lock_fd, module, strlen(module)); if (ret ! (int)strlen(module)) { printf(%s write to wakelock file failed %d - %s, __func__, ret, strerror(errno)); } else printf(module:%s write to wake_lock file success,module); close(wake_lock_fd); } static void do_wake_unlock(char *module) { char wake_unlock_path[] /sys/power/wake_unlock; int wake_unlock_fd -1; int ret -1; wake_unlock_fd open(wake_unlock_path, O_WRONLY | O_APPEND); if (wake_unlock_fd 0) { printf(%s Failed to open wakelock file - %s, __func__, strerror(errno)); } ret write(wake_unlock_fd, module, strlen(module)); if (ret ! (int)strlen(module)) { printf(%s write to wakelock file failed %d - %s, __func__, ret, strerror(errno)); } else printf(module:%s write to wake_unlock file success,module); close(wake_unlock_fd); }