Linux wakelock与autosleep
2016-06-21 11:41
330 查看
内核版本:Linux-4.5
. wakelock
Linux kernel在3.4及以后的版本中加入了wakelock机制,作用同之前的Android内核一样:
1. 为应用层提供接口来创建、释放wakelock
2. 在driver中,同样也可以创建、释放wakelock
3. 当所有的wakelock都释放掉之后,系统可以自动进入低功耗状态,由autosleep来实现
从应用层角度来看wakelock并没有什么变化,但是从底层实现角度来看,却并不完全一样。早期Android的内核版本中只是在suspend的流程上加了一把锁,只有当所有的锁释放掉之后,系统才能进入suspend状态,而自3.4之后的Linux内核版本中,是基于wakeup source实现的,创建wakelock本质上是active一个wakeup event,释放wakelock本质上是deactive wakeup event。
来看wake_lock、wake_unlock的sys接口函数:
#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 */lock、unlock的show接口直接调用的pm_show_wakelocks,只是参数不一样。
再来看lock的写接口,直接调用pm_wake_lock()函数。
首先来判断进程是否具有阻止系统进入suspend的权限。
然后获取传递进来的时间值,调用wakelock_lookup_add()函数。在wakelock_lookup_add()函数中首先查找是否具有相同name的wakelock,如果找到,则直接返回该wakelock,否则需要重新申请一个wakelock。
如果指定了timerout值,那么调用__pm_wakeup_event()去上报一个有时限的wakeup event,否则调用__pm_stay_awake()上报一个没有时限的wakeup event。
最后来看unlock的写接口。
从上面代码可以看出wakelock的核心是wakeup source,通过active一个wakeup event来阻止系统进入suspend,通过deactive wakeup event来释放wakelock。
. autosleep
autosleep的sys接口还是在kernel/power/main.c中创建的,读写函数如下:
在写函数中,直接调用autosleep.c中的pm_autosleep_set_state()函数。
在pm_autosleep_set_state()函数中,首先调用__pm_stay_awake()函数,保证系统不能休眠,保存sleep这个状态,然后根据这个state去做相应处理。
如果是suspend状态,那么首先调用pm_wakep_autosleep_enabled()去使能autosleep,紧接着调用一个workqueue,也就是调用try_to_suspend()函数。
然后调用pm_save_wakeup_count()保存这个wakeup count,调用hibernate()或pm_suspend()让系统进入suspend。
参考:http://www.wowotech.net/pm_subsystem/wakelocks.html
http://www.wowotech.net/pm_subsystem/autosleep.html
. wakelock
Linux kernel在3.4及以后的版本中加入了wakelock机制,作用同之前的Android内核一样:
1. 为应用层提供接口来创建、释放wakelock
2. 在driver中,同样也可以创建、释放wakelock
3. 当所有的wakelock都释放掉之后,系统可以自动进入低功耗状态,由autosleep来实现
从应用层角度来看wakelock并没有什么变化,但是从底层实现角度来看,却并不完全一样。早期Android的内核版本中只是在suspend的流程上加了一把锁,只有当所有的锁释放掉之后,系统才能进入suspend状态,而自3.4之后的Linux内核版本中,是基于wakeup source实现的,创建wakelock本质上是active一个wakeup event,释放wakelock本质上是deactive wakeup event。
来看wake_lock、wake_unlock的sys接口函数:
#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 */lock、unlock的show接口直接调用的pm_show_wakelocks,只是参数不一样。
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); }查询红黑树,返回处于active、deactive的wakelock。
再来看lock的写接口,直接调用pm_wake_lock()函数。
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; }例如往/sys/power/wake_lock文件写入wake_lock_test 1000,第一个参数为wakelock name,第二个参数为一个时间值,用空格隔开,单位为ns。
首先来判断进程是否具有阻止系统进入suspend的权限。
然后获取传递进来的时间值,调用wakelock_lookup_add()函数。在wakelock_lookup_add()函数中首先查找是否具有相同name的wakelock,如果找到,则直接返回该wakelock,否则需要重新申请一个wakelock。
如果指定了timerout值,那么调用__pm_wakeup_event()去上报一个有时限的wakeup event,否则调用__pm_stay_awake()上报一个没有时限的wakeup event。
最后来看unlock的写接口。
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; }例如往/sys/power/wake_unlock文件写入wake_lock_test,解析该字符串,同样调用wakelock_lookup_add()函数,但是注意第三个参数为false,查找是否有这个name的wakelock,如果找到,则返回这个wakelock,否则的话直接返回。然后调用__pm_relax()函数去deactive一个wakeup source。
从上面代码可以看出wakelock的核心是wakeup source,通过active一个wakeup event来阻止系统进入suspend,通过deactive wakeup event来释放wakelock。
. autosleep
autosleep的sys接口还是在kernel/power/main.c中创建的,读写函数如下:
#ifdef CONFIG_PM_AUTOSLEEP static ssize_t autosleep_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { suspend_state_t state = pm_autosleep_state(); if (state == PM_SUSPEND_ON) return sprintf(buf, "off\n"); #ifdef CONFIG_SUSPEND if (state < PM_SUSPEND_MAX) return sprintf(buf, "%s\n", pm_states[state] ? pm_states[state] : "error"); #endif #ifdef CONFIG_HIBERNATION return sprintf(buf, "disk\n"); #else return sprintf(buf, "error"); #endif } static ssize_t autosleep_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { suspend_state_t state = decode_state(buf, n); int error; if (state == PM_SUSPEND_ON && strcmp(buf, "off") && strcmp(buf, "off\n")) return -EINVAL; error = pm_autosleep_set_state(state); return error ? error : n; } power_attr(autosleep); #endif /* CONFIG_PM_AUTOSLEEP */在屏亮情况下,查看/sys/power/autosleep文件的值为off,在屏灭情况下去查看该值通常为mem,当我们按下关机按钮之后会往该属性文件写入mem值来触发suspend流程。
在写函数中,直接调用autosleep.c中的pm_autosleep_set_state()函数。
int pm_autosleep_set_state(suspend_state_t state) { #ifndef CONFIG_HIBERNATION if (state >= PM_SUSPEND_MAX) return -EINVAL; #endif __pm_stay_awake(autosleep_ws); mutex_lock(&autosleep_lock); autosleep_state = state; __pm_relax(autosleep_ws); if (state > PM_SUSPEND_ON) { pm_wakep_autosleep_enabled(true); queue_up_suspend_work(); } else { pm_wakep_autosleep_enabled(false); } mutex_unlock(&autosleep_lock); return 0; }pm_autosleep_set_state()根据名字来看是设置sleep的一个状态,这个状态可以是on、freeze、standby、mem等,mem就是通常所说的suspend to memory,还有一个更为深度的休眠是suspend to disk。
在pm_autosleep_set_state()函数中,首先调用__pm_stay_awake()函数,保证系统不能休眠,保存sleep这个状态,然后根据这个state去做相应处理。
如果是suspend状态,那么首先调用pm_wakep_autosleep_enabled()去使能autosleep,紧接着调用一个workqueue,也就是调用try_to_suspend()函数。
static void try_to_suspend(struct work_struct *work) { unsigned int initial_count, final_count; if (!pm_get_wakeup_count(&initial_count, true)) goto out; mutex_lock(&autosleep_lock); if (!pm_save_wakeup_count(initial_count) || system_state != SYSTEM_RUNNING) { mutex_unlock(&autosleep_lock); goto out; } if (autosleep_state == PM_SUSPEND_ON) { mutex_unlock(&autosleep_lock); return; } if (autosleep_state >= PM_SUSPEND_MAX) hibernate(); else pm_suspend(autosleep_state); mutex_unlock(&autosleep_lock); if (!pm_get_wakeup_count(&final_count, false)) goto out; /* * If the wakeup occured for an unknown reason, wait to prevent the * system from trying to suspend and waking up in a tight loop. */ if (final_count == initial_count) schedule_timeout_uninterruptible(HZ / 2); out: queue_up_suspend_work(); }在try_to_suspend()函数中,首先调用pm_get_wakeup_count()函数去获取wakeup count,如果有active的wakeup source,这里会block住,直到所有的wakeup soucre变成deactive状态,如果你的系统不能进入suspend,那么多半是这里的问题。
然后调用pm_save_wakeup_count()保存这个wakeup count,调用hibernate()或pm_suspend()让系统进入suspend。
参考:http://www.wowotech.net/pm_subsystem/wakelocks.html
http://www.wowotech.net/pm_subsystem/autosleep.html
相关文章推荐
- linux的文件系统及节点表
- CentOS_6.6上, FastDFS分布式文件系统搭建和Java测试
- Linux初步学习
- CentOs MySQL 数据库主从同步配置
- 推荐-Vsftpd虚拟用户的配置 for CentOS
- linux syslog详解-------未测试
- linux 下 pip 安装教程
- 通过SQLyog远程管理linux下mysql过程中遇到的问题以及解决方法
- 第6章 构建可启动的系统镜像
- linux lsof命令详解
- CentOS6.5修改MySQL初始密码
- zabbix3.0于centos7搭建
- >不乱码思维流程
- CentOS常用操作(CentOS 7X)
- linux服务器安装laravel教程
- 在LINUX服务器下查看公网IP地址
- linux系统中的三个时间
- linux sar命令
- linux系统相关模块
- CentOS 7 手册