电源管理常见问题:唤醒锁泄漏、Wakeup源配置错误与中断冲突
做Android电源管理这些年,我踩过的坑真不少。今天聊三个最让人头疼的问题——唤醒锁泄漏、Wakeup源配置错误、中断冲突。说白了,这三个问题就像三座大山,翻不过去,你的设备就别想省电,也别想稳定。
一、唤醒锁泄漏:看不见的电老虎
唤醒锁泄漏,是我在项目中遇到最多的问题。你想想看,一个App申请了唤醒锁,用完却不释放,系统就没法进入深度睡眠。电池就这么被白白耗掉了。
核心原因:申请了wake lock,但未在合适的时机释放。
我见过最典型的场景——某个视频App播放完视频后,后台线程还持有一个PARTIAL_WAKE_LOCK。用户以为退出了,实际上CPU一直在跑。一晚上能掉电30%。
怎么排查?我习惯用这个命令:
adb shell dumpsys power
输出里找Wake Locks部分,看看有没有长时间持有的锁。更直接的办法:
adb shell dumpsys batterystats --reset
# 使用一段时间后
adb shell dumpsys batterystats | grep "Wake lock"
| 唤醒锁类型 | 泄漏后果 | 典型场景 |
|---|---|---|
| PARTIAL_WAKE_LOCK | CPU持续运行,无法休眠 | 后台下载、定位服务 |
| FULL_WAKE_LOCK | 屏幕+CPU全开 | 视频播放、游戏 |
| SCREEN_DIM_WAKE_LOCK | 屏幕半亮,无法灭屏 | 阅读类App |
我的经验:写代码时,我习惯用try-finally块来保证释放。或者用acquire带超时参数——acquire(long timeout),这样就算忘了释放,系统也会帮你收尾。
二、Wakeup源配置错误:手机睡死了
这个问题比泄漏更可怕。唤醒锁泄漏只是费电,Wakeup源配错了——设备直接叫不醒。
我记得有一次,客户反馈说手机充电时偶尔无法唤醒屏幕。查了两天,最后发现是GPIO配置问题。某个按键的中断引脚,在休眠时被拉低了,导致系统无法检测到唤醒事件。
Wakeup源的核心逻辑其实不复杂:
// 设备树中配置唤醒源
wakeup-source;
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
// 驱动中使能唤醒
device_init_wakeup(&pdev->dev, true);
enable_irq_wake(irq);
这里有个坑——enable_irq_wake必须在中断注册之后调用。我曾经因为顺序搞反,调试了一整个下午。
注意:不是所有中断都适合做唤醒源。比如触摸屏的中断,如果配置成唤醒源,放在口袋里可能会被误触唤醒。我建议只把电源键、耳机插拔、USB插入这类关键事件配成唤醒源。
常见的Wakeup源配置错误:
- 设备树中缺少
wakeup-source属性 - GPIO的pull-up/pull-down配置与硬件不匹配
- 中断触发方式错误(边沿触发 vs 电平触发)
- 多个唤醒源共享同一个中断号
三、中断冲突:系统不稳定的元凶
中断冲突这个问题,说白了就是多个设备抢同一个中断线。你想想看,两个外设共用一根中断线,一个发信号,另一个也响应,系统不崩才怪。
我遇到过最离谱的一次——TP和指纹模块共用中断线。手指按在指纹上,触摸屏也跟着乱跳。最后只能改硬件,把中断线分开。
软件层面怎么排查?看/proc/interrupts:
cat /proc/interrupts
CPU0 CPU1
29: 1234 5678 GIC-0 47 edge gpio_keys
30: 0 0 GIC-0 48 level touchscreen
如果两个设备的中断号相同,那就是共享中断。共享本身没问题,但处理不好就会出乱子。
中断共享的黄金法则:每个中断处理函数必须能区分是不是自己的设备触发了中断。做不到这一点,就别共享。
我习惯在中断处理函数开头加一个状态检查:
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
struct my_device *dev = dev_id;
// 检查是不是自己的设备触发了中断
if (!(readl(dev->regs + INT_STATUS) & dev->int_mask)) {
return IRQ_NONE; // 不是我的,返回
}
// 处理中断
...
return IRQ_HANDLED;
}
这样做的好处是——即使共享中断,也不会误处理别人的事件。
知识体系总览
下面这张图,是我梳理的这三个问题的核心逻辑。你看一眼就能明白它们之间的关系:
你看这张图就明白了——这三个问题虽然表现不同,但最终都指向同一个结果:用户体验严重下降。而且它们之间还会互相影响。比如中断冲突可能导致唤醒源失效,唤醒源失效又可能让系统误以为有唤醒锁泄漏。
我的建议:遇到电源管理问题,别急着改代码。先看dumpsys power和/proc/interrupts,把现状摸清楚。我曾经花了一周追一个bug,最后发现只是GPIO配置少了一个上拉电阻。
嗯,这三个问题今天就聊到这儿。每个问题背后都有不少细节,但核心思路是一样的——理解硬件行为,检查软件配置,用工具验证。做到这三点,大部分问题都能迎刃而解。