Android电源管理基础知识整理

转载自: https://www.jianshu.com/p/cc553c9c75b0?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes

一、标准Linux睡眠唤醒机制简介:

休眠主要三个主要的步骤:
(1)冻结用户态进程和内核态任务;
(2)调用注册的设备的suspend的回调函数,其调用顺序是按照驱动加载时的注册顺序。
(3)休眠核心设备和使CPU进入休眠态 冻结进程:内核把进程列表中所有的进程的状态都设置为停止,并且保存下所有进程的上下文。
当这些进程被解冻的时候,它们是不知道自己被冻结过的,只是简单的继续执行。

那么是如何让Linux进入休眠的呢?其实很简单,因为Android和kernel已经做了很多复杂的工作,用户只需通过读写sys文件/sys /power/state 实现控制系统进入休眠。比如:
#echo mem > /sys/power/state /// 使系统进行睡眠
#echo on > /sys/power/state使系统从睡眠中唤醒过来
当然还有其它的状态操作,在下面的内容中将有介绍。
Linux系统电源状态
在Linux操作系统中,将电源划分为如下几个状态:
ACPI State Linux State Description
S0 On(on) Working
S1 Standby(standby) CPU and RAM are powered but not executed
S2 ------ ------
S3 Suspend to RAM(mem) CPU is Off,RAM is powered and the running content is saved to RAM
S4 Suspend to Disk(disk) All content is saved to Disk and power down
S5 Shutdown Shutdown the system
On:正常工作状态
STR(Suspend to RAM):
挂起到内存,俗称待机、睡眠(Sleep),进入该状态,系统的主要工作如下:
1、将系统当前的运行状态等数据保存在内存中,此时仍需要向RAM供电,以保证后续快速恢复至工作状态
2、冻结用户态的进程和内核态的任务(进入内核态的进程或内核自己的task)
3、关闭外围设备,如显示屏、鼠标等,中断唤醒外设不会关闭,如电源键
4、CPU停止工作

Standby:
也属于睡眠的一种方式,属于浅睡眠。该模式下CPU并未断电,依旧可以接收处理某些特定事件,视具体设备而定,恢复至正常工作状态的速度也比STR更快,但也更为耗电。举个例子来说,以该方式进入睡眠时,后续通过点击键盘也能将系统唤醒。而以mem进入的睡眠为深度睡眠,只能通过中断唤醒设备唤醒系统,如电源键(此时按电源键,不会经过正常的开机流程的BIOS、BOOTLOAD等),此时按键盘是无法唤醒系统的。
STD(Suspend to Disk):
挂起到硬盘,俗称休眠(Hibernation)将系统当前的运行状态等数据保存到硬盘上,并自动关机。下次开机时便从硬盘上读取之前保存的数据,恢复到休眠关机之前的状态。
譬如在休眠关机时,桌面打开了一个应用,那么下一次开机启动时,该应用也处于打开状态。而正常的关机-开机流程,该应用是不会打开的。
睡眠与休眠是2个不同的概念,睡眠属于STR,而休眠属于STD。所以准确来说,安卓只有睡眠,没有休眠(已关机,安卓已经不再运行)

二、Android睡眠唤醒机制简介:

Idle State
Android上的Idle状态分为二类:Cpu Idle和Device Idle
Cpu Idle
Linux系统运行的基础是基于进程调度,实际上内核调度的线程(task),内核并不会区分线程与进程,都将他们当做一个线程(task)来处理;当所有的进程都没事儿干的时候,系统就会启用idle进程,使系统进入低功耗状态(如关闭一些服务、模块功能,降低CPU工作频率等),即idle状态,以达到省电的目的。
idle状态又可以划分为不同的层级,以MTK的芯片为例,通常划分为以下几个状态:
状态 描述
soidle(screen on idle) 亮屏 Idle 模式,该模式下与正常工作状态差别不大,唯一的区别就cpu处于空闲状态
rgidle 浅度 Idle 模式,cpu处于 WFI(wait for interrupt),屏幕熄灭,同时关闭一些不需要的服务及模块,注意此状态cpu的时钟源与RTC模块是工作正常的,此时是可以通过TimerTask的定时触发激活系统的,TimerTask依赖于CPU的RTC模块,而Alarm则依赖于PMIC的RTC模块
dpidle(deep idle) 深度idle模式,该模式下cpu的时钟源和hrtimer(高精度定时器模块(RTC))被关闭,所有进程(包括系统进程)被冻结,即进入上文所述的睡眠状态
Device Idle
Device Idle属于Doze模式中概念,即指当手机屏幕熄屏、不充电、静置不动,有网友分析了源码,指出6.0手机需要静置1时4分30秒才能进入Doze模式。
Doze模式的限制
网络接入被暂停
系统忽略wake locks
标准的AlarmManager alarms(包括setExact()和setWindow())被延缓到下一个maintenance window
如果你需要在Doze状态下启动设置的alarms,使用setAndAllowWhileIdle()或者setExactAndAllowWhileIdle()。当有setAlarmClock()的alarms启动时,系统会短暂退出Doze模式
系统不会扫描Wi-Fi
系统不允许sync adapters运行
系统不允许JobScheduler运行
结合上文分析的cpu idle不难发现Doze模式中的idle状态在概念属于浅idle状态,只是关闭了一些特定服务和模块,并非立即进入睡眠,当然这个过程当中依旧有可能满足睡眠条件而进入睡眠状态
Android电源管理框架
Android采用linux内核,所以电源状态整体上是与linux操作系统相同,下图是Android的电源管理框架:

WakeLock
唤醒锁,一种锁机制,用于阻止系统进入睡眠状态,只要有应用获取到改锁,那么系统就无法进入睡眠状态。
该机制起初是早期Android为Linux内核打得一个补丁,并想合入到linux内核,但被Linux社区拒绝,后续Linux内核引入自己的Wakelock机制,Android系统也使用的是linux的Wakelock机制,所以该机制并非Android特有的机制。
Android系统提供了两种类型的锁,每一个类型又可分为超时锁与普通锁,超时锁,超时会自动释放,而普通锁则必需要手动释放:
类型 描述
WAKE_LOCK_SUSPEND 阻止系统进入睡眠状态(STR)
WAKE_LOCK_IDLE 阻止系统从idle进程进入那些具有较大中断时延、禁用了较多中断源的低功耗状态(睡眠除外),持有该类型的锁,不影响系统进入睡眠状态。自Android API-17(对应android linux内核版本3.4)移除了该类型的唤醒锁。
中断时延:计算机接收到中断信号到操作系统作出响应,并完成转入中断服务程序(ISR)的时间。
应用层提供的锁类型如下,这些锁都需要手动释放:
FLAG CPU 屏幕 键盘
PARTIAL_WAKE_LOCK 开启 关闭 关闭
SCREEN_DIM_WAKE_LOCK 开启 变暗 关闭
SCREEN_BRIGHT_WAKE_LOCK 开启 变亮 关闭
FULL_WAKE_LOCK 开启 变亮 变亮

锁的释放
Linux3.4内核中摒弃了之前的wakelock机制,引入wakeup source机制来进行睡眠管理,为了保证上层接口不变,Android的Linux内核便将wakeup source包装成wakelock
当我们应用层释放锁之后,它并不会马上消失。wakelock分为激活和非激活状态,非激活状态300S之内,无人在申请wakelock,那么它将从红黑二叉树,LRU链表当中删除,如此便可复用锁,节省系统开销。
睡眠触发入口
在wakelock中,有3个地方可以让系统从early_suspend进入suspend状态。

  1. wake_unlock,系统每释放一个锁,就会检查是否还存其他激活的wakelock,若不存在则执行Linux的标准suspend流程进入睡眠状态
  2. 在超时锁的超时回调函数,判断是否存在其他激活的wakelock,若不存在,则进入睡眠状态
  3. autosleep机制,android 4.1引入该机制,亮屏时会向autosleep节点写入off,熄屏则会写入mem。Android一灭屏,就会尝试进入睡眠,失败之后系统处于idle进程超过一定时间,则又尝试进入睡眠,判断标准同上,若存在wakelock则进入失败

关于autosleep机制的内核源码分析,可以参考如下文章:
Android autosleep机制
Early Suspend
预挂起机制是Android特有的挂起机制, 这个机制作用是关闭一些与显示相关的外设,比如LCD背光、重力感应器、 触摸屏,但是其他外设如WIFI、蓝牙等模块等并未关闭。
此时,系统依旧可以处理事件,如音乐播放软件,息屏后依旧能播放音乐。
需要注意的是Early Suspend机制与WakeLock机制相互独立,就算有应用持有wakelock锁,系统依旧可以通过Early Suspend机制关闭与显示相关的外设。
注意:
Android 4.4起,也就是引入ART的版本,摒弃了early suspend机制,改用了fb event通知机制,即后续版本只有suspend、resume以及runtime suspend、runtime resume。
Late Resume
迟唤醒机制,用于唤醒预挂起的设备
睡眠状态转换
一般情况下,当我们息屏后,系统将先通过Early Suspend机制进入Idle状态,如果满足进入睡眠的条件(没有进程持有唤醒锁)则会通过Linux的Suspend机制进入Sleep(睡眠)状态。

睡眠状态转换

内核源码流程分析可参考如下文章:
源码位于kernel_common/kernel/power/main.c:
Android中休眠与唤醒之wake_lock, early_suspend, late_resume
看到这儿,不知你是否疑问,既然系统睡眠了,CPU断电不执行指令了,为何我们定的Alarm会生效以及能接收到来电?
手机来电与Alarm为何能唤醒系统
原来Android在硬件架构上将处理器分为二类:Application Processor(AP)和Baseband Processor(BP),AP是ARM架构的处理器,用于运行Linux+Android系统,耗电量高;BP用于运行实时操作系统(RTOS),用于处理手机通信,耗电量低。

性能优化典范截图
当AP进入睡眠,有来电时,Modem(调制解调器)将唤醒AP;而我们平时所用的Alarm在硬件上则是依赖PMIC(电源管理芯片)中的RTC模块,所以即使AP断电进入睡眠,我们定的闹钟依旧会生效。

若想更深入的了解,则可参考Android RIL机制相关的文章。
总结

  1. 待机、睡眠与休眠的区别
    实际上待机(standby)与睡眠(mem)属于不同模式,但现在大多操作系统都不支持待机模式了,我们也习惯将待机等同于睡眠,睡眠属于STR,休眠属于STD,Android手机不支持休眠!!!
  2. Android开发者官网当中提到“idle state”,该如何理解,这个状态会对设备及我们的程序造成何种影响
    所谓的idle状态,就是指系统进入某个低功耗状态,以MTK为例,常见的状态有soidle、rgidle以及dpidle。rgidle只是限制我们程序使用某些模块,如Doze模式中不能访问网络;而dpidle则会冻结所有进程,系统进入睡眠。
  3. 进入Doze模式中的idle状态,我们的程序还能运行吗?
    Doze模式中的idle概念上属于rgidle状态,此时我们的程序是能运行的,只是不能访问网络等,但是在这个过程中,系统可能会满足进入睡眠条件,冻结所有进程,这样我们的程序就不会得到执行。
    可以自己写个死循环的线程(普通线程,非looper线程),强制手机进入Doze的idle模式,你会发现你的程序依旧在执行,但是静置在哪儿一段时间后,你会发现你的线程被冻结,不会执行,当你点亮屏幕,你的线程又会继续工作。
  4. 手机睡眠之后,为何我们写Alarm程序、来电显示程序依旧会生效?
    Android在硬件架构上将处理器分为AP与BP,应用程序运行与AP之中,睡眠只是将AP断电,BP(Modem)不会断电,当有来电时,BP将会唤醒AP。
    Alarm在硬件上依赖的是Modem中的PMIC的RTC模块,而不是AP中的RTC模块,当定时器触发时,可以唤醒AP,使我们的Alarm程序依旧会得到执行

Android在Linux内核唤醒模块基础上,增加了下面三个机制:
Wake _Lock 唤醒锁机制;
Early _Suspend 预挂起机制(浅睡眠);
Late _Resume 迟唤醒机制;

基本原理:当启动一个应用程序的时候,可以申请一个wake_lock唤醒锁,申请成功后都会在内核中注册一下(通知系统内核,已经有锁被申请),当应用程序释放wake_lock时,会注销之前申请的wake_lock。
特别注意:系统中有一个wake_lock,系统都不能进行睡眠。但各个模块可以early_suspend。当系统中所有的wake_lock都被释放之后,系统就会进入真正的kernel的睡眠状态。
系统启动的时候会创建一个主唤醒锁main_wake_lock,该锁是内核初始化并持有的一个WAKE_LOCK_SUSPEND属性的非限时唤醒锁。系统正常工作时,将始终因为该锁被内核持有无法进入睡眠状态。不添加新锁的情况下,只需将main_wake_lock 解锁,系统即可进入睡眠状态。
Android睡眠唤醒模块框架

   从Android最上层(Java写的应用程序)往下跟进,经过Java、C++和C语言写的Framework层、JNI层、HAL层最后到达android的最底层(Kernel层)。

主要涉及目录文件:
frameworks/base/core/java/android/os/PowerManager.java
frameworks/base/services/java/com/android/server/PowerManagerService.java
frameworks/base/services/jni/com_android_server_power_PowerManagerService.cpp
hardware/libhardware_legacy/power/power.c

kernel/power/main.c
kernel/power/earlysuspend.c
kernel/power/suspend.c
kernel/power/wakelock_android.c
kernel/power/userwakelock.c

  在应用程序框架层中,PowerManager类是面向上层应用程序的接口类,提供了Wake Lock机制(同时也是睡眠唤醒子系统)的基本接口(唤醒锁的获取和释放)。上层应用程序通过调用这些接口,实现对系统电源状态的监控。
  PowerManager类通过IBinder这种Android中特有的通信模式,与PowerManagerService 类进行通信。PowerManagerService调用Power 类来与下一层进行通信。PowerManagerService 类是WakeLock 机制在应用程序框架层的核心,他们对应用程调用PowerManager类接口时所传递的参数进行初步的分析和对应的设置,并管理一个唤醒锁队列,然后配合其他模块(例如WatchDog、BatteryService、ShutdownThread 等)的状态信息,做出决策,调用Power类的对应接口,最终通过JNI 接口,调用到硬件抽象层中的函数,对sysfs 的用户接口进行操作,从而触发内核。

PowerManager.java:提供上层应用程序的接口;
PowerManagerService.java:具体实现PowerManager类中的接口;
com_android_server_power_PowerManagerService.cpp:实现Power类中的JNI接口;
power.c:进行sysfs用户接口的操作。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值