android 休眠唤醒机制分析(一) — wake_lock

2023-11-15

Android的休眠唤醒主要基于wake_lock机制,只要系统中存在任一有效的wake_lock,系统就不能进入深度休眠,但可以进行设备的浅度休眠操作。wake_lock一般在关闭lcd、tp但系统仍然需要正常运行的情况下使用,比如听歌、传输很大的文件等。本文主要分析driver层wake_lock的实现。

一、wake_lock 定义和接口

enum {
	WAKE_LOCK_SUSPEND, // 阻止进入深度休眠模式
	WAKE_LOCK_IDLE,    // 阻止进入空闲模式
	WAKE_LOCK_TYPE_COUNT
};

struct wake_lock {
#ifdef CONFIG_HAS_WAKELOCK
	struct list_head    link;     // 链表节点
	int                 flags;    // 标志
	const char         *name;     // 名称
	unsigned long       expires;  // 超时时间
#ifdef CONFIG_WAKELOCK_STAT
	struct {
		int             count;         // 使用计数
		int             expire_count;  // 超时计数
		int             wakeup_count;  // 唤醒计数
		ktime_t         total_time;    // 锁使用时间
		ktime_t         prevent_suspend_time;  // 锁阻止休眠的时间
		ktime_t         max_time;      // 锁使用时间最长的一次
		ktime_t         last_time;     // 锁上次操作时间
	} stat;
#endif
#endif
};

可以看到wake_lock按功能分为休眠锁和空闲锁两种类型,用于阻止系统进入深度休眠模式或者空闲模式。wake_lock的主要部件有锁名称、链表节点、标志位、超时时间,另外还有一个内嵌的结构用于统计锁的使用信息。接下来我们看看wake_lock对外提供的操作接口:

1、内核空间接口

void wake_lock_init(struct wake_lock *lock, int type, const char *name);
void wake_lock_destroy(struct wake_lock *lock);
void wake_lock(struct wake_lock *lock);
void wake_lock_timeout(struct wake_lock *lock, long timeout);
void wake_unlock(struct wake_lock *lock);
其中wake_lock_init()用于初始化一个新锁,type参数指定了锁的类型;wake_lock_destroy()则注销一个锁;wake_lock()和wake_lock_timeout()用于将初始化完成的锁激活,使之成为有效的永久锁或者超时锁;wake_unlock()用于解锁使之成为无效锁。另外还有两个接口:

int wake_lock_active(struct wake_lock *lock);
long has_wake_lock(int type);

其中wake_lock_active()用于判断锁当前是否有效,如果有效则返回非0值;has_wake_lock()用于判断系统中是否还存在有效的type型锁,如果存在超时锁则返回最长的一个锁的超时时间,如果存在永久锁则返回-1,如果系统中不存在有效锁则返回0。

2、用户空间接口

wake_lock向用户空间提供了两个文件节点用于申请锁和解锁:

// wack_lock文件的读函数,显示用户空间定义的有效锁
ssize_t wake_lock_show(
	struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
	char *s = buf;
	char *end = buf + PAGE_SIZE;
	struct rb_node *n;
	struct user_wake_lock *l;

	mutex_lock(&tree_lock);

	for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
		l = rb_entry(n, struct user_wake_lock, node);
		if (wake_lock_active(&l->wake_lock))
			s += scnprintf(s, end - s, "%s ", l->name);
	}
	s += scnprintf(s, end - s, "\n");

	mutex_unlock(&tree_lock);
	return (s - buf);
}

// wack_lock文件的写函数,初始化并激活用户空间定义的锁
ssize_t wake_lock_store(
	struct kobject *kobj, struct kobj_attribute *attr,
	const char *buf, size_t n)
{
	long timeout;
	struct user_wake_lock *l;

	mutex_lock(&tree_lock);
	l = lookup_wake_lock_name(buf, 1, &timeout);
	if (IS_ERR(l)) {
		n = PTR_ERR(l);
		goto bad_name;
	}

	if (debug_mask & DEBUG_ACCESS)
		pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);

	if (timeout)
		wake_lock_timeout(&l->wake_lock, timeout);
	else
		wake_lock(&l->wake_lock);
bad_name:
	mutex_unlock(&tree_lock);
	return n;
}

// wack_unlock文件的读函数,显示用户空间的无效锁
ssize_t wake_unlock_show(
	struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
	char *s = buf;
	char *end = buf + PAGE_SIZE;
	struct rb_node *n;
	struct user_wake_lock *l;

	mutex_lock(&tree_lock);

	for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
		l = rb_entry(n, struct user_wake_lock, node);
		if (!wake_lock_active(&l->wake_lock))
			s += scnprintf(s, end - s, "%s ", l->name);
	}
	s += scnprintf(s, end - s, "\n");

	mutex_unlock(&tree_lock);
	return (s - buf);
}

// wack_unlock文件的写函数,用于用户空间解锁
ssize_t wake_unlock_store(
	struct kobject *kobj, struct kobj_attribute *attr,
	const char *buf, size_t n)
{
	struct user_wake_lock *l;

	mutex_lock(&tree_lock);
	l = lookup_wake_lock_name(buf, 0, NULL);
	if (IS_ERR(l)) {
		n = PTR_ERR(l);
		goto not_found;
	}

	if (debug_mask & DEBUG_ACCESS)
		pr_info("wake_unlock_store: %s\n", l->name);

	wake_unlock(&l->wake_lock);
not_found:
	mutex_unlock(&tree_lock);
	return n;
}

power_attr(wake_lock);
power_attr(wake_unlock);
这两个文件节点分别为"/sys/power/wake_lock"和"/sys/power/wake_unlock",应用程序可以根据HAL层的接口读写这两个节点。
二、wake_lock 实现
在linux/kernel/power/wakelock.c中我们可以看到wake_lock的实现代码,首先看看其定义的一些初始化信息:

#define WAKE_LOCK_TYPE_MASK              (0x0f)     // 锁类型标志掩码
#define WAKE_LOCK_INITIALIZED            (1U << 8)  // 锁已经初始化标志
#define WAKE_LOCK_ACTIVE                 (1U << 9)  // 锁有效标志
#define WAKE_LOCK_AUTO_EXPIRE            (1U << 10) // 超时锁标志
#define WAKE_LOCK_PREVENTING_SUSPEND     (1U << 11) // 正在阻止休眠标志

static DEFINE_SPINLOCK(list_lock);  // 读写锁链表的自旋锁
static LIST_HEAD(inactive_locks);   // 内核维护的无效锁链表
static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];  // 有效锁链表
static int current_event_num;       // 休眠锁使用计数器
struct workqueue_struct *suspend_work_queue;  // 执行系统休眠的工作队列
struct workqueue_struct *sys_sync_work_queue; // 执行系统同步的工作队列
struct wake_lock main_wake_lock;              // 内核休眠锁
struct wake_lock sys_sync_wake_lock;          // 缓存同步锁
suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;  // 系统休眠状态
static struct wake_lock unknown_wakeup;       // 未知锁

在后面的分析中我们会看到这些变量的具体用途。

1、wake_lock系统初始化

static int __init wakelocks_init(void)
{
	int ret;
	int i;
	// 初始化有效锁链表,内核维护了2个有效锁链表
	// WAKE_LOCK_SUSPEND 用于阻止进入深度休眠模式
	// WAKE_LOCK_IDLE    用于阻止进入空闲模式
	for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
		INIT_LIST_HEAD(&active_wake_locks[i]);

#ifdef CONFIG_WAKELOCK_STAT
	// 初始化deleted_wake_locks
	wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,
			"deleted_wake_locks");
#endif
	// 初始化内核休眠锁
	wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
	// 初始化同步锁
	wake_lock_init(&sys_sync_wake_lock, WAKE_LOCK_SUSPEND, "sys_sync");
	// 激活内核休眠锁
	wake_lock(&main_wake_lock);
	// 初始化未知锁
	wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");

	// 注册power_device,power_driver
	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;
	}
	// 创建fs_sync内核进程
	sys_sync_work_queue = create_singlethread_workqueue("fs_sync");
	if (sys_sync_work_queue == NULL) {
		pr_err ("fs_sync workqueue create failed.\n");
	}
	// 创建suspend内核进程
	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_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_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(&unknown_wakeup);
	wake_lock_destroy(&main_wake_lock);
#ifdef CONFIG_WAKELOCK_STAT
	wake_lock_destroy(&deleted_wake_locks);
#endif
	return ret;
}
core_initcall(wakelocks_init);

可以看到内核通过core_initcall调用了wake_lock系统的初始化函数,函数首先初始化了两个有效锁的链表,用于管理系统中的有效锁;接下来初始化了deleted_wake_locks用于处理统计信息,main_wake_lock用于锁定内核(系统启动时会激活这个锁,深度休眠时需要释放这个锁),sys_sync_wake_lock用于浅度休眠阶段同步缓存时阻止内核进入深度休眠,unknown_wakeup用于唤醒时延迟0.5s进入下一次可能的深度休眠;还注册了一个platform_device用于深度休眠阶段检测是否存在有效锁;后面创建了内核进程fs_sync用于浅度休眠阶段同步缓存,内核进程suspend用于进行浅度休眠和深度休眠;还在/proc下面创建了wakelocks节点用于显示wake_lock的统计信息。

2、wake_lock初始化

void wake_lock_init(struct wake_lock *lock, int type, const char *name)
{
	unsigned long irqflags = 0;
	// 初始化名称
	if (name)
		lock->name = name;
	BUG_ON(!lock->name);

	if (debug_mask & DEBUG_WAKE_LOCK)
		pr_info("wake_lock_init name=%s\n", lock->name);
#ifdef CONFIG_WAKELOCK_STAT
	lock->stat.count = 0;
	lock->stat.expire_count = 0;
	lock->stat.wakeup_count = 0;
	lock->stat.total_time = ktime_set(0, 0);
	lock->stat.prevent_suspend_time = ktime_set(0, 0);
	lock->stat.max_time = ktime_set(0, 0);
	lock->stat.last_time = ktime_set(0, 0);
#endif
	// 初始化flag
	lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;
	// 初始化链表节点
	INIT_LIST_HEAD(&lock->link);
	spin_lock_irqsave(&list_lock, irqflags);
	// 将锁加入无效锁链表
	list_add(&lock->link, &inactive_locks);
	spin_unlock_irqrestore(&list_lock, irqflags);
}
EXPORT_SYMBOL(wake_lock_init);
其中参数lock为被初始化对象,type代表锁的类型,name表示锁的名称, 函数主要初始化锁的名称并设置 WAKE_LOCK_INITIALIZED 标志位,并将锁加入无效锁链表inactive_locks,当需要使用锁的时候通过wake_lock()或者wake_lock_timeout()激活该锁:

// 根据参数激活锁
static void wake_lock_internal(
	struct wake_lock *lock, long timeout, int has_timeout)
{
	int type;
	unsigned long irqflags;
	long expire_in;

	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
	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);
		// 设置锁超时时间,以当前jiffies为基准
		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++;  // 休眠锁使用计数器加1
#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;
		// 当前存在有效超时锁,并且最长的一个到期时间间隔为expire_in
		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);
			if (expire_in == 0)  // 无有效锁
				queue_work(suspend_work_queue, &suspend_work);
		}
	}
	spin_unlock_irqrestore(&list_lock, irqflags);
}

// 激活永久锁
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);
}
EXPORT_SYMBOL(wake_lock_timeout);
可以看到激活过程都是通过调用wake_lock_internal()完成的,该函数首先完成一些统计信息的初始化,设置 WAKE_LOCK_ACTIVE 标志位并将锁从无效锁链表中移除;然后根据是否是超时锁设置 WAKE_LOCK_AUTO_EXPIRE 标志位,并设置超时锁的超时时间,再将锁加入有效锁链表;最后再根据锁的类型判断是否为休眠锁,如果是休眠锁且为超时锁则通过has_wake_lock_locked()获取系统中存在的超时锁中时间最长的到期时间值,并以此值设置expire_timer,has_wake_lock_locked()返回0则表示系统中不存在有效锁则启动suspend进程开始进入深度休眠状态。

3、expire_timer

static void expire_wake_locks(unsigned long data)
{
	long has_lock;
	unsigned long irqflags;
	if (debug_mask & DEBUG_EXPIRE)
		pr_info("expire_wake_locks: start\n");
	spin_lock_irqsave(&list_lock, irqflags);
	// 打印当前的有效锁
	if (debug_mask & DEBUG_SUSPEND)
		print_active_locks(WAKE_LOCK_SUSPEND);
	// 检测系统是否持有休眠锁
	has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);
	if (debug_mask & DEBUG_EXPIRE)
		pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);
	// 如果系统当前没有持有有效地休眠锁
	if (has_lock == 0)
		// 则启动深度休眠工作队列
		queue_work(suspend_work_queue, &suspend_work);
	spin_unlock_irqrestore(&list_lock, irqflags);
}
// 定义timer,运行函数为expire_wake_locks
static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
该timer会在多个地方用到,在激活锁的函数中注册用于超时锁到期后检测系统的有效锁状态,如果系统不存在有效锁了则启动suspend进程。
4、suspend_work
static void suspend(struct work_struct *work)
{
	int ret;
	int entry_event_num;

	// 判断系统是否还持有有效锁,如果有则直接返回
	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");
	// 开始深度休眠
	ret = pm_suspend(requested_suspend_state);
	// 退出深度休眠,打印信息
	if (debug_mask & DEBUG_EXIT_SUSPEND) {
		struct timespec ts;
		struct rtc_time tm;
		getnstimeofday(&ts);
		rtc_time_to_tm(ts.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.tv_nsec);
	}
	// 如果深度休眠前和深度休眠后锁的使用次数一致,即唤醒过程中没有激活新的锁
	if (current_event_num == entry_event_num) {
		if (debug_mask & DEBUG_SUSPEND)
			pr_info("suspend: pm_suspend returned with no event\n");
		// 激活unknown_wakeup,0.5s超时
		wake_lock_timeout(&unknown_wakeup, HZ / 2);
	}
}
// 声明工作队列,运行函数为suspend
static DECLARE_WORK(suspend_work, suspend);
声明工作队列用于内核深度休眠,可以看到一个正常的休眠流程会三次调用sys_sync()用于同步缓存(之前一次在浅度休眠,之后一次在深度休眠),然后调用pm_suspend()开始执行深度休眠流程。
5、has_wake_lock

// 移除过期超时锁
static void expire_wake_lock(struct wake_lock *lock)
{
#ifdef CONFIG_WAKELOCK_STAT
	wake_unlock_stat_locked(lock, 1);
#endif
	// 清除锁有效和超时锁标志
	lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
	// 从当前链表中删除
	list_del(&lock->link);
	// 加入无效锁链表
	list_add(&lock->link, &inactive_locks);
	if (debug_mask & (DEBUG_WAKE_LOCK | DEBUG_EXPIRE))
		pr_info("expired wake lock %s\n", lock->name);
}

// 打印有效锁信息,调用者需持有list_lock
static void print_active_locks(int type)
{
	struct wake_lock *lock;
	bool print_expired = true;

	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
	// 遍历有效锁链表
	list_for_each_entry(lock, &active_wake_locks[type], link) {
		// 如果是超时锁
		if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
			// 计算超时剩余时间
			long timeout = lock->expires - jiffies;
			if (timeout > 0)
				pr_info("active wake lock %s, time left %ld\n",
					lock->name, timeout);
			else if (print_expired)
				pr_info("wake lock %s, expired\n", lock->name);
		} else {  // 如果不是超时锁
			pr_info("active wake lock %s\n", lock->name);
			if (!debug_mask & DEBUG_EXPIRE)
				print_expired = false;
		}
	}
}

static long has_wake_lock_locked(int type)
{
	struct wake_lock *lock, *n;
	long max_timeout = 0;

	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
	// 遍历有效锁链表
	list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
		// 如果是超时锁
		if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
			// 计算超时剩余时间
			long timeout = lock->expires - jiffies;
			// 如果锁已经过期
			if (timeout <= 0)
				// 移除过期锁
				expire_wake_lock(lock);
			else if (timeout > max_timeout)  // 如果锁没有过期
				// 得到最长的一个超时时间
				max_timeout = timeout;
		} else // 如果不是超时锁则返回-1
			return -1;
	}
	return max_timeout;
}

// 判断系统是否还持有有效锁
long has_wake_lock(int type)
{
	long ret;
	unsigned long irqflags;
	spin_lock_irqsave(&list_lock, irqflags);
	// 开始判断流程
	ret = has_wake_lock_locked(type);
	// 如果还有休眠锁有效则打印状态信息
	if (ret && (debug_mask & DEBUG_SUSPEND) && type == WAKE_LOCK_SUSPEND)
		print_active_locks(type);
	spin_unlock_irqrestore(&list_lock, irqflags);
	return ret;
}
has_wake_lock()为系统判断当前是否存在指定类型有效锁的接口,在has_wake_lock_locked()中遍历有效锁链表,返回前面我们已经说明的值;并且打印所有有效锁的状态信息。
6、wake_unlock

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);
	// 将锁从有效锁链表中移除加入无效锁链表
	list_del(&lock->link);
	list_add(&lock->link, &inactive_locks);
	// 如果是休眠锁
	if (type == WAKE_LOCK_SUSPEND) {
		// 判断系统当前是否还持有锁
		long has_lock = has_wake_lock_locked(type);
		// 如果还持有锁,设置timer到超时时间点触发
		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))  // 删除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);
}
EXPORT_SYMBOL(wake_unlock);
该函数用于释放一个锁,首先将锁从有效锁链表中移除并加入无效锁链表,并判断系统是否还持有有效锁,如果没有则进入深度休眠流程。

7、wake_lock_active

// 判断锁是否有效
int wake_lock_active(struct wake_lock *lock)
{
	return !!(lock->flags & WAKE_LOCK_ACTIVE);
}
EXPORT_SYMBOL(wake_lock_active);
8、wake_lock_destroy

void wake_lock_destroy(struct wake_lock *lock)
{
	unsigned long irqflags;
	if (debug_mask & DEBUG_WAKE_LOCK)
		pr_info("wake_lock_destroy name=%s\n", lock->name);
	spin_lock_irqsave(&list_lock, irqflags);
	// 清除已经初始化的标志
	lock->flags &= ~WAKE_LOCK_INITIALIZED;
#ifdef CONFIG_WAKELOCK_STAT
	if (lock->stat.count) {
		deleted_wake_locks.stat.count += lock->stat.count;
		deleted_wake_locks.stat.expire_count += lock->stat.expire_count;
		deleted_wake_locks.stat.total_time =
			ktime_add(deleted_wake_locks.stat.total_time,
				  lock->stat.total_time);
		deleted_wake_locks.stat.prevent_suspend_time =
			ktime_add(deleted_wake_locks.stat.prevent_suspend_time,
				  lock->stat.prevent_suspend_time);
		deleted_wake_locks.stat.max_time =
			ktime_add(deleted_wake_locks.stat.max_time,
				  lock->stat.max_time);
	}
#endif
	// 从当前链表中删除
	list_del(&lock->link);
	spin_unlock_irqrestore(&list_lock, irqflags);
}
EXPORT_SYMBOL(wake_lock_destroy);
该函数用于注销wake_lock,首先清除 WAKE_LOCK_INITIALIZED 标志位,然后更新统计信息,最后将锁从链表中删除。

9、proc节点

// 获取锁的剩余超时时间,通过*expire_time传递
int get_expired_time(struct wake_lock *lock, ktime_t *expire_time)
{
	struct timespec ts;
	struct timespec kt;
	struct timespec tomono;
	struct timespec delta;
	unsigned long seq;
	long timeout;

	// 如果不是超时锁则直接返回
	if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE))
		return 0;

	do {
		seq = read_seqbegin(&xtime_lock);
		// 计算超时时间点与当前时间的差值
		timeout = lock->expires - jiffies;
		// 如果时间没有到期,返回0
		if (timeout > 0)
			return 0;
		// 获取当前时间
		kt = current_kernel_time();
		tomono = wall_to_monotonic;
	} while (read_seqretry(&xtime_lock, seq));
	// 时间格式转换
	jiffies_to_timespec(-timeout, &delta);
	// 设置timespec的成员
	set_normalized_timespec(&ts, kt.tv_sec + tomono.tv_sec - delta.tv_sec,
				kt.tv_nsec + tomono.tv_nsec - delta.tv_nsec);
	// 返回ts值
	*expire_time = timespec_to_ktime(ts);
	return 1;
}

// 打印出锁的状态信息
static int print_lock_stat(struct seq_file *m, struct wake_lock *lock)
{
	int lock_count = lock->stat.count;
	int expire_count = lock->stat.expire_count;
	ktime_t active_time = ktime_set(0, 0);
	ktime_t total_time = lock->stat.total_time;
	ktime_t max_time = lock->stat.max_time;

	ktime_t prevent_suspend_time = lock->stat.prevent_suspend_time;
	// 如果锁有效
	if (lock->flags & WAKE_LOCK_ACTIVE) {
		ktime_t now, add_time;
		// 获取超时剩余时间
		int expired = get_expired_time(lock, &now);
		if (!expired)
			now = ktime_get();
		// 计算当前时间和上次操作时间的差值
		add_time = ktime_sub(now, lock->stat.last_time);
		lock_count++;  // 使用计数加1
		if (!expired)  // 如果没有到期
			active_time = add_time;
		else  // 锁已经到期
			expire_count++;  // 超时计数加1
		total_time = ktime_add(total_time, add_time);  // 锁使用时间增加
		if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND)
			prevent_suspend_time = ktime_add(prevent_suspend_time,
					ktime_sub(now, last_sleep_time_update));
		if (add_time.tv64 > max_time.tv64)
			max_time = add_time;
	}

	return seq_printf(m,
		     "\"%s\"\t%d\t%d\t%d\t%lld\t%lld\t%lld\t%lld\t%lld\n",
		     lock->name, lock_count, expire_count,
		     lock->stat.wakeup_count, ktime_to_ns(active_time),
		     ktime_to_ns(total_time),
		     ktime_to_ns(prevent_suspend_time), ktime_to_ns(max_time),
		     ktime_to_ns(lock->stat.last_time));
}

// 打印锁状态
static int wakelock_stats_show(struct seq_file *m, void *unused)
{
	unsigned long irqflags;
	struct wake_lock *lock;
	int ret;
	int type;

	spin_lock_irqsave(&list_lock, irqflags);
	// 输出菜单
	ret = seq_puts(m, "name\tcount\texpire_count\twake_count\tactive_since"
			"\ttotal_time\tsleep_time\tmax_time\tlast_change\n");
	// 遍历无效锁链表并打印锁的状态信息
	list_for_each_entry(lock, &inactive_locks, link)
		ret = print_lock_stat(m, lock);
	// 遍历有效锁链表并打印锁的状态信息
	for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) {
		list_for_each_entry(lock, &active_wake_locks[type], link)
			ret = print_lock_stat(m, lock);
	}
	spin_unlock_irqrestore(&list_lock, irqflags);
	return 0;
}

// proc文件打开函数,调用show函数显示当前所有的锁信息
static int wakelock_stats_open(struct inode *inode, struct file *file)
{
	return single_open(file, wakelock_stats_show, NULL);
}

// proc文件系统操作函数
static const struct file_operations wakelock_stats_fops = {
	.owner = THIS_MODULE,
	.open = wakelock_stats_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
};

以上是proc节点的操作接口,在wakelocks_init中注册。


总结:通过以上分析我们可以看到启动深度休眠流程有四个可能的地方,分别为expire_timer、wake_lock、wake_lock_timeout、wake_unlock,其中expire_timer和wake_unlock最常见。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android 休眠唤醒机制分析(一) — wake_lock 的相关文章

  • Android - 从资产中解析巨大(超大)JSON 文件的最佳方法

    我正在尝试从资产文件夹中解析一些巨大的 JSON 文件 我如何加载并添加到 RecyclerView 我想知道解析这种大文件 大约 6MB 的最佳方法是什么 以及您是否知道可以帮助我处理此文件的良好 API 我建议您使用GSON lib h
  • 如何以编程方式检查 AndroidManifest.xml 中是否声明了服务?

    我正在编写一个库 该库提供了一项服务 其他开发人员可以通过将其包含在他们的项目中来使用该服务 因此 我无法控制 AndroidManifest xml 我在文档中解释了要做什么 但一个常见的问题是人们忽略了将适当的 标记添加到其清单中 或者
  • 是否可以将数组或对象添加到 Android 上的 SharedPreferences

    我有一个ArrayList具有名称和图标指针的对象 我想将其保存在SharedPreferences 我能怎么做 注意 我不想使用数据库 无论 API 级别如何 请检查SharedPreferences 中的字符串数组和对象数组 http
  • 内置方案以检查列表包含情况

    在Python中 我可以执行 x in list 来查看列表是否包含x 方案中是否有等效的内置功能可以做到这一点 The R5RS http schemers org Documents Standards R5RS HTML r5rs Z
  • C 位域内存使用情况

    我需要处理以下形式的一些数据 typedef struct unsigned n1 12 unsigned n2 12 unsigned n3 12 unsigned n4 1 unsigned n5 35 data 我确保它们总共最多有
  • 找不到处理意图 com.instagram.share.ADD_TO_STORY 的活动

    在我们的 React Native 应用程序中 我们试图让用户根据视图 组件中的选择直接将特定图像共享到提要或故事 当我们尝试直接使用 com instagram share ADD TO FEED 进行共享时 它以一致的方式完美运行 但是
  • Android 模拟器插件无法初始化后端 EGL 显示

    我在 Cloudbees 上设置了 Jenkins 作业 并且可以在那里成功签出并编译我的 Android 项目 现在我想在 android 模拟器中运行一些 JUnit 测试并添加 Android 模拟器插件 我将 显示模拟器窗口 选项设
  • 在 java 类和 android 活动之间传输时音频不清晰

    我有一个android活动 它连接到一个java类并以套接字的形式向它发送数据包 该类接收声音数据包并将它们扔到 PC 扬声器 该代码运行良好 但在 PC 扬声器中播放声音时会出现持续的抖动 中断 安卓活动 public class Sen
  • 无法访问 com.google.android.gms.internal.zzbfm 的 zzbfm 类文件未找到

    我正在将我的 Android 应用程序项目从GCM to FCM 为此 我使用 Android Studio 中的 Firebase 助手工具 并遵循 Google 开发人员指南中的说明 一切都很顺利 并将我的应用程序代码更改为FCM根据助
  • Android:捕获的图像未显示在图库中(媒体扫描仪意图不起作用)

    我遇到以下问题 我正在开发一个应用程序 用户可以在其中拍照 附加到帖子中 并将图片保存到外部存储中 我希望这张照片也显示在图片库中 并且我正在使用媒体扫描仪意图 但它似乎不起作用 我在编写代码时遵循官方的Android开发人员指南 所以我不
  • 尝试将相机切换回前面但出现异常

    尝试将相机切换回前面 但出现异常 找不到 问题请检查并帮助 error 01 27 11 49 00 376 E AndroidRuntime 30767 java lang RuntimeException Unable to start
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 如何使用 IF 检查 TextView 可见性

    我有一个 onCheckedChangeListener 来根据选择的单选按钮显示文本视图 我有 1 个疑问和 1 个难题 想知道是否有人可以帮助我 问题 您能否将单选组默认检查值设置为 否 单选按钮 以便一开始就不会检查任何内容 问题 如
  • Ubuntu 16.04 - Genymotion:找不到 /dev/hw_random

    I install Genymotion on the Ubuntu 16 04 64Bit I created a virtual emulator for Android 6 0 then I run this emulator but
  • 我想实现下面的布局,按钮应该在屏幕底部,当惰性列被填充时,按钮不应该出去

    顶部有惰性列 惰性列下方有输入电话号码布局并从电话簿布局添加联系人 我希望当未添加联系人时此布局位于顶部 当我添加大量联系人时输入电话号码并添加电话簿布局中的联系人会随着惰性列滚动并移出屏幕 我不让他们走出屏幕 当接触较多时 它们必须粘在底
  • 在 android DatePickerDialog 中将语言设置为法语

    有什么办法可以让日期显示在DatePickerDialog用法语 我已经搜索过这个但没有找到结果 这是我的代码 Calendar c Calendar getInstance picker new DatePickerDialog Paym
  • 在activity_main.xml中注释

    我是安卓新手 据我所知 XML 中的注释与 HTML 中的注释相同 使用 形式 我想在 Android 项目的 Activity main xml 配置文件中写一些注释 但它给了我错误 值得注意的是 我使用的是 Eclipse 但目前 我直
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两

随机推荐