若是你也有以上疑问,那么本文会对你解开疑惑有必定的帮助html
要理解第一个问题,得先从ACPI(高级配置与电源接口)提及,ACPI是一种规范(包含软件与硬件),用来供操做系统应用程序管理全部电源接口。linux
ACPI将计算机系统的状态划分为四个全局状态(G0-G3),共7个状态,其中G0对应S0;G1将低功耗状态细分为四个状态,对应S1-S4;G二、G3表明关机状态分别对应S五、S6。android
ACPI State | Description |
---|---|
S0 | 正常工做状态 |
S1 | CPU与RAM供电正常,但CPU不执行指令 |
S2 | 比S1更深的一个睡眠层次,这种模式一般不采用 |
S3 | 挂起到内存 |
S4 | 挂起到硬盘 |
S5 | Soft Off,CPU、外设等断电,但电源依旧会为部分极低耗设备供电 |
S6 | Mechanical Off,所有断电 |
这里只须要对ACPI的七个状态有个大体了解便可,下一节会有具体的例子来讲明各个状态。git
在Linux操做系统中,将电源划分为以下几个状态:github
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:正常工做状态网络
挂起到内存,俗称待机、睡眠(Sleep),进入该状态,系统的主要工做以下:数据结构
一、将系统当前的运行状态等数据保存在内存中,此时仍须要向RAM供电,以保证后续快速恢复至工做状态架构
二、冻结用户态的进程和内核态的任务(进入内核态的进程或内核本身的task)框架
三、关闭外围设备,如显示屏、鼠标等,中断唤醒外设不会关闭,如电源键函数
四、CPU中止工做
Standby也属于睡眠的一种方式,属于浅睡眠。该模式下CPU并未断电,依旧能够接收处理某些特定事件,视具体设备而定,恢复至正常工做状态的速度也比STR更快,但也更为耗电。举个例子来讲,以该方式进入睡眠时,后续经过点击键盘也能将系统唤醒。而以mem进入的睡眠为深度睡眠,只能经过中断唤醒设备唤醒系统,如电源键(此时按电源键,不会通过正常的开机流程的BIOS、BOOTLOAD等),此时按键盘是没法唤醒系统的。
挂起到硬盘,俗称休眠(Hibernation)将系统当前的运行状态等数据保存到硬盘上,并自动关机。下次开机时便从硬盘上读取以前保存的数据,恢复到休眠关机以前的状态。
譬如在休眠关机时,桌面打开了一个应用,那么下一次开机启动时,该应用也处于打开状态。而正常的关机-开机流程,该应用是不会打开的。
Linux内核代码声明以下,位于kernel/power/suspend.c
在新版内核中,进程freeze的功能被单独抽离出来做为一个电源状态,该状态仅仅是冻结进程,并不会使系统进入低功耗状态(如切断CPU时钟源、关闭外设供电等)。
相关宏定义位于:linux/include/linux/suspend.h
其中状态4就是STD,所谓的休眠状态(Hibernation)
至此,咱们能够知道,睡眠与休眠是2个不一样的概念,睡眠属于STR,而休眠属于STD,切勿混为一谈。
网上也有不少关于“Android休眠”的文章,事实上,Android手机压根儿就不支持休眠模式。
#查看系统支持的电源模式 $ cat /sys/power/state #休眠系统命令 $ sudo pm-hibernate
看来Ubuntu-17.0.4版本是不支持休眠功能了,state当中并无disk,执行休眠命令也提示找不到。
在公司测试Ubuntu-16.0.4是支持休眠的,休眠时会将当前RAM中的数据保持至swap分区,以供后续恢复。
这里我使用的是模拟器查看的,真机也同样,Android手机是不支持休眠模式的,休眠模式须要一块与RAM大小一致存储空间,这在移动设备上但是个不小的开销。
Android上的Idle状态分为二类:Cpu Idle和Device 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))被关闭,全部进程(包括系统进程)被冻结,即进入上文所述的睡眠状态 |
idle进程是由原始进程(pid=0)在初始化init进程(pid=1)以后演变而来,能够说是init进程的祖先,关于其详细介绍可参考以下连接:
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采用linux内核,因此电源状态总体上是与linux操做系统相同,下图是Android的电源管理框架:
唤醒锁,一种锁机制,用于阻止系统进入睡眠状态,只要有应用获取到改锁,那么系统就没法进入睡眠状态。
该机制起初是早期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)的时间。
内核当中关于WakeLock的主要源码位于:
kernel_common/include/linux/wakelock.h
kernel_common/kernel/power/wakelock.c
Android Linux内核3.0版本
Android Linux内核3.4版本
应用层提供的锁类型以下,这些锁都须要手动释放:
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的数据结构以下:
wakelock数据结构
当咱们应用层释放锁以后,它并不会立刻消失。wakelock分为激活和非激活状态,非激活状态300S以内,无人在申请wakelock,那么它将从红黑二叉树,LRU链表当中删除,如此即可复用锁,节省系统开销。
在wakelock中,有3个地方可让系统从early_suspend进入suspend状态。
wake_unlock,系统每释放一个锁,就会检查是否还存其余激活的wakelock,若不存在则执行Linux的标准suspend流程进入睡眠状态
在超时锁的超时回调函数,判断是否存在其余激活的wakelock,若不存在,则进入睡眠状态
autosleep机制,android 4.1引入该机制,亮屏时会向autosleep节点写入off,熄屏则会写入mem。Android一灭屏,就会尝试进入睡眠,失败以后系统处于idle进程超过必定时间,则又尝试进入睡眠,判断标准同上,若存在wakelock则进入失败
关于autosleep机制的内核源码分析,能够参考以下文章:
预挂起机制是Android特有的挂起机制, 这个机制做用是关闭一些与显示相关的外设,好比LCD背光、重力感应器、 触摸屏,可是其余外设如WIFI、蓝牙等模块等并未关闭。
此时,系统依旧能够处理事件,如音乐播放软件,息屏后依旧能播放音乐。
须要注意的是Early Suspend机制与WakeLock机制相互独立,就算有应用持有wakelock锁,系统依旧能够经过Early Suspend机制关闭与显示相关的外设。
注意:
Android 4.4起,也就是引入ART的版本,摒弃了early suspend机制,改用了fb event通知机制,即后续版本只有suspend、resume以及runtime suspend、runtime resume。
迟唤醒机制,用于唤醒预挂起的设备
通常状况下,当咱们息屏后,系统将先经过Early Suspend机制进入Idle状态,若是知足进入睡眠的条件(没有进程持有唤醒锁)则会经过Linux的Suspend机制进入Sleep(睡眠)状态。
内核源码流程分析可参考以下文章:
源码位于kernel_common/kernel/power/main.c:
Android中休眠与唤醒之wake_lock, early_suspend, late_resume
看到这儿,不知你是否疑问,既然系统睡眠了,CPU断电不执行指令了,为什么咱们定的Alarm会生效以及能接收到来电?
原来Android在硬件架构上将处理器分为二类:Application Processor(AP)和Baseband Processor(BP),AP是ARM架构的处理器,用于运行Linux+Android系统,耗电量高;BP用于运行实时操做系统(RTOS),用于处理手机通讯,耗电量低。
当AP进入睡眠,有来电时,Modem(调制解调器)将唤醒AP;而咱们平时所用的Alarm在硬件上则是依赖PMIC(电源管理芯片)中的RTC模块,因此即便AP断电进入睡眠,咱们定的闹钟依旧会生效。
若想更深刻的了解,则可参考Android RIL机制相关的文章。
实际上待机(standby)与睡眠(mem)属于不一样模式,但如今大多操做系统都不支持待机模式了,咱们也习惯将待机等同于睡眠,睡眠属于STR,休眠属于STD,Android手机不支持休眠!!!
所谓的idle状态,就是指系统进入某个低功耗状态,以MTK为例,常见的状态有soidle、rgidle以及dpidle。rgidle只是限制咱们程序使用某些模块,如Doze模式中不能访问网络;而dpidle则会冻结全部进程,系统进入睡眠。
Doze模式中的idle概念上属于rgidle状态,此时咱们的程序是能运行的,只是不能访问网络等,可是在这个过程当中,系统可能会知足进入睡眠条件,冻结全部进程,这样咱们的程序就不会获得执行。
能够本身写个死循环的线程(普通线程,非looper线程),强制手机进入Doze的idle模式,你会发现你的程序依旧在执行,可是静置在哪儿一段时间后,你会发现你的线程被冻结,不会执行,当你点亮屏幕,你的线程又会继续工做。
Android在硬件架构上将处理器分为AP与BP,应用程序运行与AP之中,睡眠只是将AP断电,BP(Modem)不会断电,当有来电时,BP将会唤醒AP。
Alarm在硬件上依赖的是Modem中的PMIC的RTC模块,而不是AP中的RTC模块,当定时器触发时,能够唤醒AP,使咱们的Alarm程序依旧会获得执行