Linux电源管理(9)_wakelocks

作者:wowo 发布于:2014-9-14 23:17

分类:电源管理子系统

1. 前言

wakelocks是一个有故事的功能。

wakelocks最初出现在Android为linux kernel打的一个补丁集上,该补丁集实现了一个名称为“wakelocks”的系统调用,该系统调用允许调用者阻止系统进入低功耗模式(如idle、suspend等)。同时,该补丁集更改了Linux kernel原生的电源管理执行过程(kernel/power/main.c中的state_show和state_store),转而执行自定义的state_show、state_store。

这种做法是相当不规范的,它是典型的只求实现功能,不择手段。就像国内很多的Linux开发团队,要实现某个功能,都不去弄清楚kernel现有的机制、框架,牛逼哄哄的猛干一番。最后功能是实现了,可都不知道重复造了多少轮子,浪费了多少资源。到此打住,Android的开发者不会这么草率,他们推出wakelocks机制一定有一些苦衷,我们就不评论了。

但是,虽然有苦衷,kernel的开发者可是有原则的,死活不让这种机制合并到kernel分支(换谁也不让啊),直到kernel自身的wakeup events framework成熟后,这种僵局才被打破。因为Android开发者想到了一个坏点子:不让合并就不让合并呗,我用你的机制(wakeup source),再实现一个就是了。至此,全新的wakelocks出现了。

所以wakelocks有两个,早期Android版本的wakelocks几乎已经销声匿迹了,不仔细找还真找不到它的source code(这里有一个链接,但愿读者看到时还有效,drivers/android/power.c)。本文不打算翻那本旧黄历,所以就focus在新的wakelocks上(kernel/power/wakelock.c,较新的kernel都支持)。

2. Android wakelocks

虽说不翻旧黄历了,还是要提一下Android wakelocks的功能,这样才能知道kernel wakelocks要做什么。总的来说,Android wakelocks提供的功能包括:

1)一个sysfs文件:/sys/power/wake_lock,用户程序向文件写入一个字符串,即可创建一个wakelock,该字符串就是wakelock的名字。该wakelock可以阻止系统进入低功耗模式。

2)一个sysfs文件::/sys/power/wake_unlock,用户程序向文件写入相同的字符串,即可注销一个wakelock。

3)当系统中所有的wakelock都注销后,系统可以自动进入低功耗状态。

4)向内核其它driver也提供了wakelock的创建和注销接口,允许driver创建wakelock以阻止睡眠、注销wakelock以允许睡眠。

有关Android wakelocks更为详细的描述,可以参考下面的一个链接:

3. Kernel wakelocks

3.1 Kernel wakelocks的功能

对比Android wakelocks要实现的功能,Linux kernel的方案是:

1)允许driver创建wakelock以阻止睡眠、注销wakelock以允许睡眠:已经由“Linux电源管理(7)_Wakeup events framework”所描述的wakeup source取代。

2)当系统中所有的wakelock都注销后,系统可以自动进入低功耗状态:由autosleep实现(下一篇文章会分析)。

3)wake_lock和wake_unlock功能:由本文所描述的kernel wakelocks实现,其本质就是将wakeup source开发到用户空间访问。

3.2 Kernel wakelocks在电源管理中的位置

相比Android wakelocks,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。

3.3 /sys/power/wake_lock & /sys/power/wake_unlock

从字面意思上,新版的wake_lock和wake_unlock和旧版的一样,都是用于创建和注销wakelock。从应用开发者的角度,确实可以这样理解。但从底层实现的角度,却完全不是一回事。

Android的wakelock,真是一个lock,用户程序创建一个wakelock,就是在系统suspend的路径上加了一把锁,注销就是解开这把锁。直到suspend路径上所有的锁都解开时,系统才可以suspend。

而Kernel的wakelock,是基于wakeup source实现的,因此创建wakelock的本质是在指定的wakeup source上activate一个wakeup event,注销wakelock的本质是deactivate wakeup event。因此,/sys/power/wake_lock和/sys/power/wake_unlock两个sysfs文件的的功能就是:

写wake_lock(以wakelock name和timeout时间为参数),相当于以wakeup source为参数调用__pm_stay_awake(或者__pm_wakeup_event),即activate wakeup event;

写wake_unlock(以wakelock name为参数),相当于以wakeup source为参数,调用__pm_relax;

读wake_lock,获取系统中所有的处于active状态的wakelock列表(也即wakeup source列表)

读wake_unlock,返回系统中所有的处于非active状态的wakelock信息(也即wakeup source列表)。

这两个sysfs文件在kernel/power/main.c中实现,如下:

1: #ifdef CONFIG_PM_WAKELOCKS

2: static ssize_t wake_lock_show(struct kobject *kobj,

3: struct kobj_attribute *attr,

4: char *buf)

5: {

6: return pm_show_wakelocks(buf, true);

7: }

8:

9: static ssize_t wake_lock_store(struct kobject *kobj,

10: struct kobj_attribute *attr,

11: const char *buf, size_t n)

12: {

13: int error = pm_wake_lock(buf);

14: return error ? error : n;

15: }

16:

17: power_attr(wake_lock);

18:

19: static ssize_t wake_unlock_show(struct kobject *kobj,

20: struct kobj_attribute *attr,

21: char *buf)

22: {

23: return pm_show_wakelocks(buf, false);

24: }

25:

26: static ssize_t wake_unlock_store(struct kobject *kobj,

27: struct kobj_attribute *attr,

28: const char *buf, size_t n)

29: {

30: int error = pm_wake_unlock(buf);

31: return error ? error : n;

32: }

33:

34: power_attr(wake_unlock);

35:

36: #endif /* CONFIG_PM_WAKELOCKS */

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中实现。

3.4 pm_wake_lock

pm_wake_lock位于kernel\power\wakelock.c中,用于上报一个wakeup event(从另一个角度,就是阻止系统suspend),代码如下:

1: int pm_wake_lock(const char *buf)

2: {

3: const char *str = buf;

4: struct wakelock *wl;

5: u64 timeout_ns = 0;

6: size_t len;

7: int ret = 0;

8:

9: if (!capable(CAP_BLOCK_SUSPEND))

10: return -EPERM;

11:

12: while (*str && !isspace(*str))

13: str++;

14:

15: len = str - buf;

16: if (!len)

17: return -EINVAL;

18:

19: if (*str && *str != '\n') {

20: /* Find out if there's a valid timeout string appended. */

21: ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);

22: if (ret)

23: return -EINVAL;

24: }

25:

26: mutex_lock(&wakelocks_lock);

27:

28: wl = wakelock_lookup_add(buf, len, true);

29: if (IS_ERR(wl)) {

30: ret = PTR_ERR(wl);

31: goto out;

32: }

33: if (timeout_ns) {

34: u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;

35:

36: do_div(timeout_ms, NSEC_PER_MSEC);

37: __pm_wakeup_event(&wl->ws, timeout_ms);

38: } else {

39: __pm_stay_awake(&wl->ws);

40: }

41:

42: wakelocks_lru_most_recent(wl);

43: out:

44: mutex_unlock(&wakelocks_lock);

45: return ret;

46: }

a)输入参数为一个字符串,如"wake_lock_test 1000”,该字符串指定上报wakeup event的wakelock name,可以在name后用空格隔开,添加一个时间值(单位为ns),表示该event的timeout值。

b)调用capable,检查当前进程是否具备阻止系统suspend的权限。

注2:capable是Linux security子系统提供的一个接口,用于权限判断。我们说过,power是系统的核心资源,理应由OS全权管理,但wakelock违反了这一原则,将阻止系统睡眠的权利给了用户空间。这样一来,用户空间程序将可以随心所欲的占用power资源,特别是用户态的程序员,天生对资源占用不敏感(这是对的),就导致该接口有被滥用的风险。不过还好,通过系统的权限管理机制,可以改善这种状态(其实不是改善,而是矛盾转移,很有可能把最终的裁决权交给用户,太糟糕了!)。

c)解析字符串,将timeout值(有的话)保存在timeout_ns中,解析name长度(len),并将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 events;否则,调用__pm_stay_awake,上报一个没有时限的wakeup event。有关这两个接口的详细说明,可参考“Linux电源管理(7)_Wakeup events framework”。

wakelock_lookup_add是内部接口,代码如下:

1: static struct wakelock *wakelock_lookup_add(const char *name, size_t len,

2: bool add_if_not_found)

3: {

4: struct rb_node **node = &wakelocks_tree.rb_node;

5: struct rb_node *parent = *node;

6: struct wakelock *wl;

7:

8: while (*node) {

9: int diff;

10:

11: parent = *node;

12: wl = rb_entry(*node, struct wakelock, node);

13: diff = strncmp(name, wl->name, len);

14: if (diff == 0) {

15: if (wl->name[len])

16: diff = -1;

17: else

18: return wl;

19: }

20: if (diff < 0)

21: node = &(*node)->rb_left;

22: else

23: node = &(*node)->rb_right;

24: }

25: if (!add_if_not_found)

26: return ERR_PTR(-EINVAL);

27:

28: if (wakelocks_limit_exceeded())

29: return ERR_PTR(-ENOSPC);

30:

31: /* Not found, we have to add a new one. */

32: wl = kzalloc(sizeof(*wl), GFP_KERNEL);

33: if (!wl)

34: return ERR_PTR(-ENOMEM);

35:

36: wl->name = kstrndup(name, len, GFP_KERNEL);

37: if (!wl->name) {

38: kfree(wl);

39: return ERR_PTR(-ENOMEM);

40: }

41: wl->ws.name = wl->name;

42: wakeup_source_add(&wl->ws);

43: rb_link_node(&wl->node, parent, node);

44: rb_insert_color(&wl->node, &wakelocks_tree);

45: wakelocks_lru_add(wl);

46: increment_wakelocks_number();

47: return wl;

48: }

在wakelock.c中,维护一个名称为wakelocks_tree的红黑树(红黑树都用上了,可以想象wakelocks曾经使用多么频繁!),所有的wakelock都保存在该tree上。因此该接口的动作是:

a)查找红黑树,如果找到name相同的wakelock,返回wakelock指针。

b)如果没找到,且add_if_not_found为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结构:

1: struct wakelock {

2: char *name;

3: struct rb_node node;

4: struct wakeup_source ws;

5: #ifdef CONFIG_PM_WAKELOCKS_GC

6: struct list_head lru;

7: #endif

8: };

非常简单:一个name指针,保存wakelock的名称;一个rb node节点,用于组成红黑树;一个wakeup source变量;如果开启了wakelocks垃圾回收功能,一个用于GC的list head。

3.5 pm_wake_unlock

pm_wake_unlock和pm_wake_lock类似,如下:

1: int pm_wake_unlock(const char *buf)

2: {

3: struct wakelock *wl;

4: size_t len;

5: int ret = 0;

6:

7: if (!capable(CAP_BLOCK_SUSPEND))

8: return -EPERM;

9:

10: len = strlen(buf);

11: if (!len)

12: return -EINVAL;

13:

14: if (buf[len-1] == '\n')

15: len--;

16:

17: if (!len)

18: return -EINVAL;

19:

20: mutex_lock(&wakelocks_lock);

21:

22: wl = wakelock_lookup_add(buf, len, false);

23: if (IS_ERR(wl)) {

24: ret = PTR_ERR(wl);

25: goto out;

26: }

27: __pm_relax(&wl->ws);

28:

29: wakelocks_lru_most_recent(wl);

30: wakelocks_gc();

31:

32: out:

33: mutex_unlock(&wakelocks_lock);

34: return ret;

35: }

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的垃圾回收动作。

3.6 pm_show_wakelocks

该接口很简单,查询红黑树,返回处于acvtive或者deactive状态的wakelock,如下:

1: ssize_t pm_show_wakelocks(char *buf, bool show_active)

2: {

3: struct rb_node *node;

4: struct wakelock *wl;

5: char *str = buf;

6: char *end = buf + PAGE_SIZE;

7:

8: mutex_lock(&wakelocks_lock);

9:

10: for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {

11: wl = rb_entry(node, struct wakelock, node);

12: if (wl->ws.active == show_active)

13: str += scnprintf(str, end - str, "%s ", wl->name);

14: }

15: if (str > buf)

16: str--;

17:

18: str += scnprintf(str, end - str, "\n");

19:

20: mutex_unlock(&wakelocks_lock);

21: return (str - buf);

22: }

1)遍历红黑树,拿到wakelock指针,判断其中的wakeup source的active变量,如果和输入变量(show_active)相符,将该wakelock的名字添加在buf中。

2)调整buf的长度和结束符,返回长度值。

3.7 wakelocks的垃圾回收机制

由上面的逻辑可知,一个wakelock的生命周期,应只存在于wakeup event的avtive时期内,因此如果它的wakeup source状态为deactive,应该销毁该wakelock。但销毁后,如果又产生wakeup events,就得重新建立。如果这种建立->销毁->建立的过程太频繁,效率就会降低。

因此,最好不销毁,保留系统所有的wakelocks(同时可以完整的保留wakelock信息),但如果wakelocks太多(特别是不活动的),将会占用很多内存,也不合理。

折衷方案,保留一些非active状态的wakelock,到一定的时机时,再销毁,这就是wakelocks的垃圾回收(GC)机制。

wakelocks GC功能可以开关(由CONFIG_PM_WAKELOCKS_GC控制),如果关闭,系统会保留所有的wakelocks,如果打开,它的处理逻辑也很简单:

1)定义一个list head,保存所有的wakelock指针,如下:

1: static LIST_HEAD(wakelocks_lru_list);

2: static unsigned int wakelocks_gc_count;

2)在wakelock结构中,嵌入一个list head(lru),用于挂入wakelocks_lru_list。可参考3.4小节的描述。

3)wakelocks_lru_list中的wakelock是按访问顺序排列的,最近访问的,靠近head位置。这是由3种操作保证的:

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的垃圾回收动作。wakelocks_gc的实现如下:

1: static void wakelocks_gc(void)

2: {

3: struct wakelock *wl, *aux;

4: ktime_t now;

5:

6: if (++wakelocks_gc_count <= WL_GC_COUNT_MAX)

7: return;

8:

9: now = ktime_get();

10: list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) {

11: u64 idle_time_ns;

12: bool active;

13:

14: spin_lock_irq(&wl->ws.lock);

15: idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));

16: active = wl->ws.active;

17: spin_unlock_irq(&wl->ws.lock);

18:

19: if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))

20: break;

21:

22: if (!active) {

23: wakeup_source_remove(&wl->ws);

24: rb_erase(&wl->node, &wakelocks_tree);

25: list_del(&wl->lru);

26: kfree(wl->name);

27: kfree(wl);

28: decrement_wakelocks_number();

29: }

30: }

31: wakelocks_gc_count = 0;

32: }

1)如果当前wakelocks的数目小于最大值(由WL_GC_COUNT_MAX配置,当前代码为100),不回收,直接返回。

2)否则,从wakelocks_lru_most_recent的尾部(最不活跃的),依次取出wakelock,判断它的idle时间(通过wakeup source lst_time和当前时间计算)是否超出预设值(由WL_GC_TIME_SEC指定,当前为300s,好长),如果超出且处于deactive状态,调用wakeup_source_remove,注销wakeup source,同时把它从红黑树、GC list中去掉,并释放memory资源。

原创文章,转发请注明出处。蜗窝科技,www.wowotech.net。

评论:

2019-10-30 12:48

有误,不是drivers/power/wakelock.c,是power/power/wakelock.c

2019-10-30 16:37

@吴兵:感谢认真的读者!感谢这几天认真的阅读和回复!

太忙了不能一一回复,抱歉哈~~

这里应该是kernel/power/wakelock.c

我改一下,再次感谢

wall.e

2019-10-29 17:14

写的真好

求学

2019-07-31 20:55

wowo:请教问题,如果wake_lock_timeout同一个锁调用两次,时间不同,以哪个为准?时间长的还是按照调用顺序?

2019-08-01 17:36

@求学:这个是Android的代码?看看代码就行了,猜测是以第二次为准。也可以写个代码测试一下看看

xuwukong

2018-09-06 17:16

实际上没有wakelock 锁了,只有wake source ,

android用的wakelock,不过是通过/sys/power/wake_lock来控制wake source? 可以这样理解吗?

2018-09-07 11:13

@xuwukong:是的。

不懂猫

2018-03-02 14:21

@wowo,请问,某个soc有个特定的timer位于始终不掉电区域,它可以做为唤醒源来唤醒ap.当ap睡下后,比如core,cluster等domain都掉电,但是在睡下去之前我需要设置这个特定的timer.以便在某段实际后唤醒ap,

那我怎么找到我要设置的那个timer呢?

Feynman

2018-01-09 22:34

蜗蜗大神,问个困惑的问题,就是设置了timeout之后,如果wakelock的等待时间已经超过了timeout的时间,但是此时设备还在工作,那么此时wakelock会被释放吗

2018-01-16 09:31

@Feynman:是的,不想这样的话,就不要设置timeout时间,驱动做完事情后,主动释放就行了。

Feynman

2018-01-19 12:04

@wowo:那既然这样的话,我们可以在设备工作的时候设置no timeout然后在判断到设备不工作之后再重新设置timeout的吧,还是调用pm_wake_lock接口重复设置

Feynman

2018-01-22 20:59

@wowo:还有个疑问就是,多个进程同时持有多个相同的wakelock锁,那么其中一个进程释放了这个wakelock锁,其他的进程应该是不用再释放了吧

2018-01-23 08:33

@Feynman:这些问题都是关于接口行为,直接去看代码比较简单粗暴,问我的话我也是去查代码,所以建议你看代码吧,这样理解会深刻一些~~~:-)

Feynman

2018-01-23 09:27

@wowo:嘿,其实我看了,只是不是很确认哈,那还想请教下,怎么确定当前设备是否已经释放wakelock,或者说已经suspend,查看/sys/power/state 吗?

2018-01-23 17:22

@Feynman:可以看这两个:/sys/power/wake_lock & /sys/power/wake_unlock

小白

2017-06-27 09:59

hi wowo,

请教一个问题,对于android来说,power manager提供api 申请/释放 wakelock,并且维护大部分userspace的wakelock,当一个申请了wakelock的app包括native程序异常崩溃,power manager通过binder 机制监听到客户端挂了,进而把这个客户端申请的wakelock remove掉。通过这种方式来保证系统中wakelock都是干净的。

对于标准linux来说,是否有这种机制?如果一个程序直接往sys文件中写入wakelock,后续崩溃了,那么这个wakelock是否就是脏的,linux对这种case是否有保证?

谢谢!

2017-06-27 10:51

@小白:个人观点,这种事儿不是kernel应该考虑的。kernel只负责提供wakelock机制,至于程序怎么用、怎么把它搞脏,那是程序的事儿。用户空间完全可以用一个单独的程序,去监控这种行为的发生(就像监控某一个程序异常退出后,再重启它一样)。总之呢,kernel里面没有这样的处理。

小白

2017-06-27 11:27

@wowo:明白,谢谢!

javier

2017-07-07 15:53

@wowo:我的理解,像资源回收的事情,kernel应该是要负责清理的,比如使用内存和打开的文件在进程异常退出的过程中是能够回收的。wakelock作为一个内核资源,理论上也是应该有kernel来清理比较。不知道这样理解对不对?

2017-07-08 13:31

@javier:是的,你的理解也有道理,不过这有一个前提:

内存、文件等资源,恰恰是和进程绑在一起,以进程为单位进行管理。而进程管理又是kernel的一大任务,所以kernel可以在进程退出的时候很方便的清理它所占用的资源。从本质上看,释放进程的资源,就是进程管理的一部分。

但其它的资源,例如这里的wakelock,其设计之初,没有要求必须和进程绑定,那么进程如果退出,kernel要怎么做才合适呢?估计最安全的做法就是什么也不做。因为用户空间的程序可以很容易的解决这个问题。

冷雨秋风少

2017-05-16 15:32

wowo:

请教下如下问题,

蓝牙模块和CPU之间通过中断触发事件,android系统睡眠状态,有蓝牙信号通讯时可以唤醒系统。但是现在却唤醒不了。中断处理的实现的代码如下,wowo大神能帮看下哪边有问题。感谢

wake_lock_init(&rda_bt_wake_lock, WAKE_LOCK_SUSPEND,"RDA_sleep_worker_wake_lock");

ret = request_irq(bt_host_wake_irq,rda_bt_host_wake_eirq_handler,IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,"rda_bt_host_wake_irq", NULL);//申请蓝牙中断

static irqreturn_t rda_bt_host_wake_eirq_handler(int irq, void *dev_id)

{

rda_bt_set_wake_lock();

return IRQ_HANDLED;

}

void rda_bt_set_wake_lock(void)

{

printk("---rda_bt_set_wake_lock  \n");

wake_lock(&rda_bt_wake_lock);

cancel_delayed_work(&rda_bt_sleep_worker);

schedule_delayed_work(&rda_bt_sleep_worker, 6 * HZ);

}

static inline void wake_lock(struct wake_lock *lock)

{

__pm_stay_awake(&lock->ws);

}

INIT_DELAYED_WORK(&rda_bt_sleep_worker, rda_bt_sleep_worker_task); //

void rda_bt_sleep_worker_task(struct work_struct *work)

{

printk("---rda_bt_sleep_worker_task end \n");

wake_unlock(&rda_bt_wake_lock);

}

static inline void wake_unlock(struct wake_lock *lock)

{

__pm_relax(&lock->ws);

}

2017-05-16 19:18

@冷雨秋风少:建议先查查在suspend之前,相应的硬件是否还能产生中断(电、时钟等)、GIC是否可以处理这个中断(查一下相应的寄存器)。如果都okay,再确认CPU是否已经醒来了?是否进入到你的中断处理了?等等。

冷雨秋风少

2017-05-16 20:08

@wowo:wowo,

wakeup状态下,中断发生OK,中断处理正常。蓝牙传输数据,无法唤醒系统,只有按power键后,系统起来后,蓝牙才能正常通讯。抓到的log如下(截取其中一小段),中断唤醒CPU,经过noirq resume和early resume,但并未wakeup,按下power键后系统才起来:

<7>PM: Entering mem sleep

<4>Suspending console(s) (use no_console_suspend to debug)

<3>rtc-rx8130 2-0032: rx8130_get_time: date 22s 58m 11h 2wd 16md 4m 117y

<6>PM: suspend of devices complete after 98.196 msecs

<6>PM: suspend devices took 0.100 seconds

<6>PM: late suspend of devices complete after 0.479 msecs

<6>PM: noirq suspend of devices complete after 0.634 msecs

<4>Disabling non-boot CPUs ...

<5>CPU1: shutdown

<5>CPU2: shutdown

<5>CPU3: shutdown

<6>Suspended for 0.000 seconds

<4>---rda_bt_host_wake_eirq_handler start//中断处理函数打印信息

<4>---rda_bt_set_wake_lock

<6>Enabling non-boot CPUs ...

<4>CPU1: Booted secondary processor

<6>CPU1 is up

<4>CPU2: Booted secondary processor

<6>CPU2 is up

<4>CPU3: Booted secondary processor

<6>CPU3 is up

<6>PM: noirq resume of devices complete after 0.351 msecs

<6>PM: early resume of devices complete after 0.395 msecs

<4>enter matrix keypad interrupt.......................//按下power键

<4>[yue.zhong:][gpio_keys_gpio_report_event][394]yue.zhong:pressed

<4>

<4>[yue.zhong:][gpio_keys_gpio_report_event][400]yue.zhong:pressed over

<4>

<4>[yue.zhong:][gpio_keys_gpio_report_event][400]yue.zhong:pressed over

<4>

<3>rtc-rx8130 2-0032: rx8130_get_time: date 38s 58m 11h 2wd 16md 4m 117y

<6>PM: resume of devices complete after 200.679 msecs

<6>PM: resume devices took 0.210 seconds

<7>PM: Finishing wakeup.

<4>Restarting tasks ... done.

<6>PM: suspend exit 2017-05-16 11:58:38.109086667 UTC

<6>period 66 ,timeout is 0, wake up is :0

<6>mma enable setting active

<6>request_suspend_state: wakeup (3->0) at 285186566700 (2017-05-16 11:58:38.149

2017-05-16 22:26

@冷雨秋风少:虽然蓝牙报了一个事件上去,但application(例如android)并没有处理这个事件,接着做后续的动作。从日志上看,application只认识key的event。你可以从这个思路出发,修改应用的逻辑,让它看到蓝牙的事件后,和key一样处理。

le

2016-05-11 15:55

请教一下,/sys/power/wake_unlock中有很多节点,但是wake_lock是空的,这是什么意思呀?

# cat wake_unlock

HVDCPD_WL KeyEvents PowerManagerService.Broadcasts PowerManagerService.Display PowerManagerService.WakeLocks qcril qcril_pre_client_init radio-interface rmt_storage_548401050688 sensor_ind tftp_server_wakelock

2016-05-11 16:12

@le:意思是这些lock都是处于unlock状态。

wake_unlock中看到的是unlock状态的东西,wake_lock中看到的是lock状态的东西。

le

2016-05-11 16:35

@wowo:unlock是怎么添加的?

比如:

# echo test > wake_unlock

# cat wake_unlock

HVDCPD_WL KeyEvents PowerManagerService.Broadcasts PowerManagerService.Display PowerManagerService.WakeLocks qcril qcril_pre_client_init radio-interface rmt_storage_548401050688 rmt_storage_548408828992 sensor_ind tftp_server_wakelock

里边没有test

2016-05-11 16:49

@le:要先lock,才能unlock。

javier

2017-07-07 15:50

@le:这里的意思,我理解是不是曾经使用过的wakelock记录在rb链表中但是没有的回收清空出去,所以现在显示在/sys/power/wake_unlock中,但是不在/sys/power/wake_lock

稻草人

2015-10-30 20:07

hi, wowo,

关于android acquire wakelock有一点不明白的地方,就是framework层在申请acquire wakelock的时候在最后逐层的调用到JNI层的时候我看到他往kernel申请的wakelock只有一种PARTIAL_WAKE_LOCK类型:

static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) {

ScopedUtfChars name(env, nameStr);

acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());

}

百思不得其解,因为我看到framework层有申请其他类型的wakelock的(比如FULL_WAKE_LOCK),为什么一层层的调用下来最后就变成了只申请PARTIAL_WAKE_LOCK了,麻烦帮忙解惑呀!

Ps:上面是android JNI层的一个函数,他调用的acquire_wake_lock是HAL层的一个函数:

acquire_wake_lock(int lock, const char* id)

{

initialize_fds();

//    ALOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id);

if (g_error) return -g_error;

int fd;

if (lock == PARTIAL_WAKE_LOCK) {

fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];

}

else {

return -EINVAL;

}

return write(fd, id, strlen(id));

}

这个函数最后write的文件节点就是这篇文章开始说的/sys/power/wake_lock节点,写完之后就调用了 wake_lock_store函数,往这篇文章所写的往下面走了。

想问的就是为什么上层下来的wakelock只能申请PARTIAL_WAKE_LOCK类型的partial wake。

2015-10-30 21:42

@稻草人:我没有读过Android的这段代码,猜测如下:

这里的代码应该是Android为了兼容几种不同Wakelock机制的实现,就像本文开头所说的那样。

g_fds数组应该保存了这几种机制所对应的不同的文件句柄,当然,现在最正宗的wakelock机制,就对应了[ACQUIRE_PARTIAL_WAKE_LOCK],也即“/sys/power/wake_lock”。

你可以看看数组中的其它fd对应什么。

至于jni中为什么只使用了ACQUIRE_PARTIAL_WAKE_LOCK,估计得问问作者了,也许是不想再节外生枝,尽量往linux原生上靠拢吧。

稻草人

2015-11-02 10:00

@wowo:@wowo:

在HAL层g_fds数组中还有一个RELEASE_WAKE_LOCK成员这个对应的是“/sys/power/wake_unlock“。

device在屏幕点亮唤醒的时候申请的应该是full wakelock,而且是上往下来申请的,所以很纳闷,为啥到下面就变成partial wakelock了,今天再加log看看~

1 2

发表评论:

昵称

邮件地址 (选填)

个人主页 (选填)

linux内核 电池管理,Linux电源管理(9)_wakelocks相关推荐

  1. linux如deepin manjaro对笔记本电脑电池的伤害解决方案:TLP:一个可以延长 Linux 笔记本电池寿命的高级电源管理工具

    TLP:一个可以延长 Linux 笔记本电池寿命的高级电源管理工具 笔记本电池是针对 Windows 操作系统进行了高度优化的,当我在笔记本电脑中使用 Windows 操作系统时,我已经意识到这一点, ...

  2. 解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】

    转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的 ...

  3. Linux内核源代码情景分析-内存管理

    用户空间的页面有下面几种: 1.普通的用户空间页面,包括进程的代码段.数据段.堆栈段.以及动态分配的"存储堆". 2.通过系统调用mmap()映射到用户空间的已打开文件的内容. 3 ...

  4. linux acpi 电源管理,ACPI电源管理

    S1,S2:待机.只关闭CPU.S1是完全加电:S2是如果CPU不活动就进入待机状态. S3:挂起到内存,关闭硬盘,其它设备处于加电等待状态. S4:休眠,内存写入硬盘后,关闭所有设备. S5:关机: ...

  5. linux内核精髓:精通linux内核必会的75个绝技,Linux内核精髓精通Linux内核必会的75个绝技 PDF 高清扫描版...

    经过近20年的发展,Linux操作系统已经成为当今最成功的开源软件之一,使用广泛,影响深远.随着Linux操作系统功能的不断丰富和完善,Linux内核的源代码也从最初的几万行增加到如今的数百万行,庞大 ...

  6. 简述arm linux内核启动流程,Linux内核启动过程和Bootloader(总述)

    1.Linux内核启动过程概述 一个嵌入式 Linux 系统从软件角度看可以分为四个部分:引导加载程序(Bootloader),Linux 内核,文件系统,应用程序.其中 Bootloader是系统启 ...

  7. 武汉linux内核好找吗,Linux内核入门

    Linux内核入门 收藏 如何获取Linux内核源代码 下载Linux内核当然要去http://www.kernel.org/了,网站提供了两种文件下载,一种是完整的Linux内核,另一种是内核增量补 ...

  8. 编译Linux内核没有zImage,Linux 编译系统的简单介绍与内核编译安装

    这里不只是讲怎样编译.安装Linux内核的,更主要的是介绍内核的编译系统和各个重要的文件.最后还利用学到的编译.安装Linux内核去修改Linux的01调度变成随机调度.如果你只是需要编译.安装内核的 ...

  9. Linux内核及主流Linux发行版对应关系汇总

    Linux内核及主流Linux发行版对应关系汇总 如需转载请标明出处:http://blog.csdn.net/itas109 QQ技术交流群:129518033 文章目录 Linux内核及主流Lin ...

  10. linux内核 can总线,Linux Canbus调试笔记

    http://blog.csdn.net/flydream0/article/details/8161418 CAN总线在嵌入式Linux下驱动程序的实现 http://www.21ic.com/ap ...

最新文章

  1. Spring Cloud(二): 注册中心Eureka的使用
  2. python怎么删除特定文件_如何使用python从文件中删除特定行?
  3. 小流域水土保持遥感信息系统
  4. 被新基建点名的大数据,有哪些书最值得读?
  5. 一种通用的动作解决方案
  6. UcOS-II 和linux比较
  7. QT开发pjsip的VOIP,A8平台运行
  8. GIS + 现代农业”,将会擦出怎样的火花?——智慧农业专题论坛侧记
  9. 简约html5动态个人简历,HTML5 简约风格的程序员简历模板
  10. android无线投屏到电视盒子,【沙发管家】教你如何把电脑视频投屏到智能电视/电视盒子上!...
  11. mysql x ix_mysql – 为什么IX-lock与InnoDB中的另一个IX-lock兼容?
  12. 我用新拟物化设计风格,做了一个App改版案例
  13. 编写一个函数,判断某个数是否为水仙花数。
  14. 视频编码的守望者--Jason Garrett-Glaser
  15. 【通信原理】实验五 基于Matlab的2ASK和2FSK调制解调
  16. 用vim编辑器在行首添加行号、序列号
  17. 海思Hi3559(十)——生成wk数据文件
  18. 2.Lambda表达式
  19. 实时数仓利器之Doris
  20. [Algorithm] 哥德巴赫猜想的验证(使用Python从0开始实现)

热门文章

  1. SQL(Structured Query Language)教程(下)
  2. SSH key的使用
  3. 电脑删除的视频怎么恢复?
  4. Oracle 的安装和配置
  5. Java代码截屏:使用 Java 代码截取电脑屏幕并保存
  6. Linux的IPC命令
  7. 咸鱼Maya笔记—灯光
  8. Python字典--练习题
  9. 怎么通过大数据技术,找到更好的人才?
  10. java压缩json_Json压缩工具