前言
- 待機、睡眠與休眠的區別?
- Android開發者官網當中提到“idle states”,該如何理解,這個狀態會對設備及我們的程式造成何種影響?
- 進入Doze模式中的idle狀態,我們的程式還能運行嗎?
- 手機睡眠之后,為何我們寫Alarm程式、來電顯示程式依舊會生效?
如果你也有以上疑問,那么本文會對你解開疑惑有一定的幫助
ACPI簡介
要理解第一個問題,得先從ACPI(高級配置與電源介面)說起,ACPI是一種規范(包含軟體與硬體),用來供作業系統應用程式管理所有電源介面,
ACPI將計算機系統的狀態劃分為四個全域狀態(G0-G3),共7個狀態,其中G0對應S0;G1將低功耗狀態細分為四個狀態,對應S1-S4;G2、G3代表關機狀態分別對應S5、S6,
| ACPI State | Description |
|---|---|
| S0 | 正常作業狀態 |
| S1 | CPU與RAM供電正常,但CPU不執行指令 |
| S2 | 比S1更深的一個睡眠層次,這種模式通常不采用 |
| S3 | 掛起到記憶體 |
| S4 | 掛起到硬碟 |
| S5 | Soft Off,CPU、外設等斷電,但電源依舊會為部分極低耗設備供電 |
| S6 | Mechanical Off,全部斷電 |
這里只需要對ACPI的七個狀態有個大致了解即可,下一節會有具體的例子來說明各個狀態,
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)將系統當前的運行狀態等資料保存到硬碟上,并自動關機,下次開機時便從硬碟上讀取之前保存的資料,恢復到休眠關機之前的狀態,
譬如在休眠關機時,桌面打開了一個應用,那么下一次開機啟動時,該應用也處于打開狀態,而正常的關機-開機流程,該應用是不會打開的,
Linux內核代碼宣告如下,位于kernel/power/suspend.c

在新版內核中,行程freeze的功能被單獨抽離出來作為一個電源狀態,該狀態僅僅是凍結行程,并不會使系統進入低功耗狀態(如切斷CPU時鐘源、關閉外設供電等),
相關宏定義位于:linux/include/linux/suspend.h

其中狀態4就是STD,所謂的休眠狀態(Hibernation)
小結:
至此,我們可以知道,睡眠與休眠是2個不同的概念,睡眠屬于STR,而休眠屬于STD,切勿混為一談,
網上也有很多關于“Android休眠”的文章,事實上,Android手機壓根兒就不支持休眠模式,
查看Linux支持的電源模式
#查看系統支持的電源模式
$ cat /sys/power/state
#休眠系統命令
$ sudo pm-hibernate

看來Ubuntu-17.0.4版本是不支持休眠功能了,state當中并沒有disk,執行休眠命令也提示找不到,
在公司測驗Ubuntu-16.0.4是支持休眠的,休眠時會將當前RAM中的資料保持至swap磁區,以供后續恢復,

查看Android支持的電源模式

這里我使用的是模擬器查看的,真機也一樣,Android手機是不支持休眠模式的,休眠模式需要一塊與RAM大小一致存盤空間,這在移動設備上可是個不小的開銷,
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))被關閉,所有行程(包括系統行程)被凍結,即進入上文所述的睡眠狀態 |
idle行程是由原始行程(pid=0)在初始化init行程(pid=1)之后演變而來,可以說是init行程的祖先,關于其詳細介紹可參考如下鏈接:
Linux Idle基礎
CPUIDLE 之低功耗定時器
Device Idle
Device Idle屬于Doze模式中概念,即指當手機螢屏熄屏、不充電、靜置不動,有網友分析了原始碼,指出6.0手機需要靜置1時4分30秒才能進入Doze模式,
| Doze模式的限制 |
|---|
| 網路接入被暫停 |
| 系統忽略wake locks |
| 標準的AlarmManager |
| 如果你需要在Doze狀態下啟動設定的alarms,使用setAndAllowWhileIdle()或者setExactAndAllowWhileIdle(),當有setAlarmClock()的alarms啟動時,系統會短暫退出Doze模式 |
| 系統不會掃描Wi-Fi |
| 系統不允許sync adapters運行 |
| 系統不允許JobScheduler運行 |
結合上文分析的cpu idle不難發現Doze模式中的idle狀態在概念屬于淺idle狀態,只是關閉了一些特定服務和模塊,并非立即進入睡眠,當然這個程序當中依舊有可能滿足睡眠條件而進入睡眠狀態,至于如何進入請參考下文【睡眠觸發入口】一節,
Android Doze模式原始碼分析
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)的時間,
內核當中關于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 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機制相關的文章,
總結
- 待機、睡眠與休眠的區別
實際上待機(standby)與睡眠(mem)屬于不同模式,但現在大多作業系統都不支持待機模式了,我們也習慣將待機等同于睡眠,睡眠屬于STR,休眠屬于STD,Android手機不支持休眠!!!
- Android開發者官網當中提到“idle state”,該如何理解,這個狀態會對設備及我們的程式造成何種影響
所謂的idle狀態,就是指系統進入某個低功耗狀態,以MTK為例,常見的狀態有soidle、rgidle以及dpidle,rgidle只是限制我們程式使用某些模塊,如Doze模式中不能訪問網路;而dpidle則會凍結所有行程,系統進入睡眠,
- 進入Doze模式中的idle狀態,我們的程式還能運行嗎?
Doze模式中的idle概念上屬于rgidle狀態,此時我們的程式是能運行的,只是不能訪問網路等,但是在這個程序中,系統可能會滿足進入睡眠條件,凍結所有行程,這樣我們的程式就不會得到執行,
可以自己寫個死回圈的執行緒(普通執行緒,非looper執行緒),強制手機進入Doze的idle模式,你會發現你的程式依舊在執行,但是靜置在哪兒一段時間后,你會發現你的執行緒被凍結,不會執行,當你點亮螢屏,你的執行緒又會繼續作業,
- 手機睡眠之后,為何我們寫Alarm程式、來電顯示程式依舊會生效?
Android在硬體架構上將處理器分為AP與BP,應用程式運行與AP之中,睡眠只是將AP斷電,BP(Modem)不會斷電,當有來電時,BP將會喚醒AP,
Alarm在硬體上依賴的是Modem中的PMIC的RTC模塊,而不是AP中的RTC模塊,當定時器觸發時,可以喚醒AP,使我們的Alarm程式依舊會得到執行
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/17223.html
標籤:嵌入式
下一篇:Fuchsia文章匯總
