赞
踩
Android wakelocks提供的功能包括:
1) 一个sysfs文件: /sys/power/wake_lock, 用户程序向文件写入一个字符串,即可创建一个wake lock, 该字符串就是wakelock的名字。 该wake lock可以阻止系统进入低功耗模式。
2) 一个sysfs文件: /sys/power/wake_unlock, 用户程序向文件写入相同的字符串, 即可注销一个wakelock。
3) 当系统中所有的wakelock都注销后, i系统可以自动进入低功耗状态
4) 向内核其他driver也提供了wakelock的创建和注销接口, 允许driver创建wakelock以阻止休眠, 注销wakelock以允许睡眠。
有关Android wakelocks更为详细的描述,参考下面一个链接:
http://elinux.org/Android_Power_Management
对比Android wakelocks要实现的功能, Linux kernel的方案是:
1) 允许driver创建wakelock以阻止睡眠, 注销wakelock以允许睡眠:已经由wakeup source取代
2) 当系统中所有的wakelock都注销后,系统可以自动进入低功耗模式:由autosleep实现
3) wake_lock和wake_unlock功能,由本文所描述的kernel wakelocks实现,其本质就是将wakeup source开发到用户空间访问。
相比Android wakelocks, Kernel wakelocks的实现非常简单,就是PM core中增加一个wakelock模块(kernel/power/wakelock.c), 该模块依赖wake events framework提供的wakeup source机制,实现用户空间的wakeup source(就是wakelocks), 并通过PM core main模块, 向用户空间提供两个同名的sysfs文件, wakelock和wake_unlock.
从字面意思上,新版的wake_lock和wake_unlock和旧版的一样,都是用于创建和注销wakelock。从应用开发者的角度,确实可以这样理解。 但从底层实现的角度,却完全不是一回事。
Android的wakelock,真是一个lock, 用户程序创建一个wakelock,就是在系统suspend的路径上加了一把锁,注销就是要解开这把锁。 直到suspend路径上所有的锁都解开时,系统才可以suspend。
而kernel的wakelock, 是基于wakeup source实现的,因此创建wakelock的本质是在指定的wakup source上activate一个wakeup events, 注销wakelock的本质是deactivate wakeup event。 因此, /sys/power/wake_lock和/sys/power/wake_unlock两个sysfs文件的功能就是:
写wake_lock(以wakelock name和timeout时间<可选>为参数), 相当于以wakeup source为参数调用__pm_stay_awake(或者__pm_wake_event), 即activate wakeup event;
写wake_unlocks(以wakelock name为参数), 相当于以wakeup source为参数, 调用__pm_relax;
读wake_lock, 获取系统中所有的处于active状态的wakelock列表(也即wakeup source列表)
写wake_unlock, 返回系统中所有的处于非active状态的wakelock信息(也即wakeup source列表)。
注1: 上面有关wakeup source的操作接口,
这两个sysfs文件在kernel/power/main.c中实现:
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; } 1) wakelocks功能不是linux kernel的必选功能,可以通过CONFIG_PM_WAKELOCKS开关 2) wake_lock的写接口,直接调用pm_wake_lock; wake_unlock的写接口,直接调用pm_wake_unlock; 他们的读接口,直接调用pm_show_wakelocks(参数不同), 这三个接口均在kernel/power/wakelock.c中实现。
pm_wake_lock位于kernel/power/wakelock.c, 用于上报一个wakeup event(从另一个角度,就是阻止 系统suspend)
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; while (*str && !isspace(*str)) str++; len = str - buf; if (!len) return -EINVAL; if (*str && *str != '\n') { /* Find out if there's a valid timeout string appended. */ ret = kstrtou64(skip_spaces(str), 10, &timeout_ns); if (ret) return -EINVAL; } mutex_lock(&wakelocks_lock); 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); out: mutex_unlock(&wakelocks_lock); return ret; } a) 输入参数为一个字符串, 如“wake_lock_test 100", 该字符串指定上报wakeup event的wakelock name, 可以在name后用空格隔开, 添加一个时间值(单位ns), 表示该event的timeout值 b) 调用capable, 查看当前进程是否具备阻止系统suspend的权限 注2: capable是Linux security子系统的一个接口,用于权限判断。 我们说过,power是系统的核心资源, 理应由OS全权掌握, 但wakelock违反了这一个原则, 将阻止系统睡眠的权力给了用户空间。 这样一来, 用户空间程序将可以随心所欲的占用power资源, 特别是用户态的程序员, 天生对资源占用不敏感的(这是对的), 就导致该接口有被滥用的风险, 不过还好,通过系统的权限管理机制,可以改善这种状态(其实不是改善,而是矛盾转移,很有可能吧最终的裁决权给到用户,太糟糕了); c)解析字符串,将timeout值保存在timeout_ns中, 解析name长度,并将name保存在原来的buf中 d) 调用wakelock_lookup_add接口, 查找是否有相同name的wakelock。 如果有,直接返回wakelock的指针; 如果没有,分配一个wakelock, 同时调用wakeup events framework提供的接口,创建该wakelock对应的wakeup source结构。 e) 如果指定timeout值,以wakelock的wakeup source指针为参数, 调用__pm_wakeup_event接口,上报一个具有时限的wakeup event; 否则调用__pm_stay_awake,上报一个没有时限的wakeup event。
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; 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; } if (diff < 0) node = &(*node)->rb_left; else node = &(*node)->rb_right; } if (!add_if_not_found) return ERR_PTR(-EINVAL); if (wakelocks_limit_exceeded()) return ERR_PTR(-ENOSPC); /* 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); } wl->ws.name = wl->name; wakeup_source_add(&wl->ws); rb_link_node(&wl->node, parent, node); rb_insert_color(&wl->node, &wakelocks_tree); wakelocks_lru_add(wl); increment_wakelocks_number(); return wl; } 在wakelock.c中,维护一个名称为wakelocks_tree的红黑树所有的wakelock都保存在该tree上。因此该接口的动作是: a) 查找红黑树,如果找到name相同的wakelock, 就返回wakelock指针 b) 如果没找到,切add_if_not_fount为false,返回错误 c) 如果add_if_not_found为true, 分配一个struct wakelock变量, 并初始化它的名称,他的wakeup source的名称。 调用wakeup_source_add接口,将wakeup source添加到wakeup events framework中 d) 将wakelock添加到红黑树 e) 最后调用wakelocks_lru_add接口,将新分配的wakeup添加到一个名称为wakelocks_lru_list的链表前端。
再看一下wakelock的结构
struct wakelock {
char *name;
struct rb_node node;
struct wakeup_source ws;
#ifdef CONFIG_PM_WAKELOCKS_GC
struct list_head lru;
#endif
};
非常简单:一个name指针,保存wakelock的名称;一个rb node节点,用于组成红黑树;一个wakeup source变量;如果开启了wakelocks垃圾回收功能,一个用于GC的list head。
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); 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); wakelocks_gc(); out: mutex_unlock(&wakelocks_lock); return ret; } a)输入参数为一个字符串,如"wake_lock_test”,该字符串指定一个wakelock name。 b)调用capable,检查当前进程是否具备阻止系统suspend的权限。 c)解析字符串 d)调用wakelock_lookup_add接口,查找是否有相同name的wakelock。如果有,直接返回wakelock的指针;如果没有,退出。 e)调用__pm_relax接口,deactive wakelock对应的wakeup source。 f)调用wakelocks_lru_most_recent接口,将盖wakelock移到wakelocks_lru_list链表的前端(表示它是最近一个被访问到的,和GC有关,后面重点描述)。 g)调用wakelocks_gc,执行wakelock的垃圾回收动作。
该接口很简单,查询红黑树,返回处于acvtive或者deactive状态的wakelock,如下:
ssize_t pm_show_wakelocks(char *buf, bool show_active) { struct rb_node *node; struct wakelock *wl; char *str = buf; char *end = buf + PAGE_SIZE; mutex_lock(&wakelocks_lock); for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) { wl = rb_entry(node, struct wakelock, node); if (wl->ws.active == show_active) str += scnprintf(str, end - str, "%s ", wl->name); } if (str > buf) str--; str += scnprintf(str, end - str, "\n"); mutex_unlock(&wakelocks_lock); return (str - buf); } 1)遍历红黑树,拿到wakelock指针,判断其中的wakeup source的active变量,如果和输入变量(show_active)相符,将该wakelock的名字添加在buf中。 2)调整buf的长度和结束符,返回长度值。
由上面的逻辑可知,一个wakelock的生命周期,应只存于wakeup event的active周期内,因此如果它的wakeup source状态为deactive, 应该销毁该wakelock。 但销毁后,如果又产生wakeup events, 就得重新建立。 如果这种建立->销毁->建立的过程太频繁, 效率就会降低。
因此,最好不销毁, 保留系统所有的wakelocks(同时可以完整的保留wakelock信息), 但如果wakelocks太多(特别是不活动的),将会占用很多内存,也不合理。
折衷方案: 保留一些非active状态的wakelock, 到一定时机时再销毁,这就是wakelocks的垃圾回收机制。
wakelocks GC功能可以开关(由CONFIG_PM_WAKELOCK_GC控制), 如果关闭,系统会保留所有的wakelocks,如果打开,它的处理逻辑也很简单:
1) 定义一个list head, 保存所有的wakelock指针,如下
static LIST_HEAD(wakelocks_lru_list);
static unsigned int wakelocks_gc_count;
2) 在wakelock结构中,嵌入一个list head(lru), 用于挂入wakelocks_lru_list
3) wakelocks_lru_list中的wakelock是按访问顺序排列的, 最近访问的,靠近head位置,这是由三种操作保证的
a)wakelock创建时(见3.4小节),调用wakelocks_lru_add接口,将改wakelock挂到wakelocks_lru_list的head处(利用list_add接口),表示它是最近被访问的。
b)pm_wake_lock或者pm_wake_unlock时,调用wakelocks_lru_most_recent接口,将该wakelcok移到链表的head处,表示最近访问。
c)每当pm_wake_unlock时,调用wakelocks_gc,执行wakelock的垃圾回收动作。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。