前言
本文主要圍繞如下問題進行知識收集整理:
- 待機况木、睡眠與休眠的區(qū)別启盛?
- Android開發(fā)者官網(wǎng)當中提到“idle states”榛了,該如何理解,這個狀態(tài)會對設備及我們的程序造成何種影響名挥?
- 進入Doze模式中的idle狀態(tài),我們的程序還能運行嗎主守?
- 手機睡眠之后禀倔,為何我們寫Alarm程序、來電顯示程序依舊會生效参淫?
如果你也有以上疑問救湖,那么本文會對你解開疑惑有一定的幫助
ACPI簡介
要理解第一個問題,得先從ACPI(高級配置與電源接口)說起黄刚,ACPI是一種規(guī)范(包含軟件與硬件)捎谨,用來供操作系統(tǒng)應用程序管理所有電源接口。
ACPI將計算機系統(tǒng)的狀態(tài)劃分為四個全局狀態(tài)(G0-G3)憔维,共7個狀態(tài)涛救,其中G0對應S0;G1將低功耗狀態(tài)細分為四個狀態(tài),對應S1-S4;G2业扒、G3代表關機狀態(tài)分別對應S5检吆、S6。
ACPI State | Description |
---|---|
S0 | 正常工作狀態(tài) |
S1 | CPU與RAM供電正常程储,但CPU不執(zhí)行指令 |
S2 | 比S1更深的一個睡眠層次蹭沛,這種模式通常不采用 |
S3 | 掛起到內(nèi)存 |
S4 | 掛起到硬盤 |
S5 | Soft Off,CPU章鲤、外設等斷電摊灭,但電源依舊會為部分極低耗設備供電 |
S6 | Mechanical Off,全部斷電 |
這里只需要對ACPI的七個狀態(tài)有個大致了解即可败徊,下一節(jié)會有具體的例子來說明各個狀態(tài)帚呼。
Linux系統(tǒng)電源狀態(tài)
在Linux操作系統(tǒng)中,將電源劃分為如下幾個狀態(tài):
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:正常工作狀態(tài)
STR(Suspend to RAM):
掛起到內(nèi)存皱蹦,俗稱待機煤杀、睡眠(Sleep),進入該狀態(tài)沪哺,系統(tǒng)的主要工作如下:
1沈自、將系統(tǒng)當前的運行狀態(tài)等數(shù)據(jù)保存在內(nèi)存中,此時仍需要向RAM供電辜妓,以保證后續(xù)快速恢復至工作狀態(tài)
2枯途、凍結用戶態(tài)的進程和內(nèi)核態(tài)的任務(進入內(nèi)核態(tài)的進程或內(nèi)核自己的task)
3忌怎、關閉外圍設備,如顯示屏柔袁、鼠標等,中斷喚醒外設不會關閉呆躲,如電源鍵
4、CPU停止工作
Standby也屬于睡眠的一種方式捶索,屬于淺睡眠插掂。該模式下CPU并未斷電,依舊可以接收處理某些特定事件腥例,視具體設備而定辅甥,恢復至正常工作狀態(tài)的速度也比STR更快,但也更為耗電燎竖。舉個例子來說璃弄,以該方式進入睡眠時,后續(xù)通過點擊鍵盤也能將系統(tǒng)喚醒构回。而以mem進入的睡眠為深度睡眠夏块,只能通過中斷喚醒設備喚醒系統(tǒng),如電源鍵(此時按電源鍵纤掸,不會經(jīng)過正常的開機流程的BIOS脐供、BOOTLOAD等),此時按鍵盤是無法喚醒系統(tǒng)的借跪。
STD(Suspend to Disk):
掛起到硬盤政己,俗稱休眠(Hibernation)將系統(tǒng)當前的運行狀態(tài)等數(shù)據(jù)保存到硬盤上,并自動關機掏愁。下次開機時便從硬盤上讀取之前保存的數(shù)據(jù)歇由,恢復到休眠關機之前的狀態(tài)。
譬如在休眠關機時果港,桌面打開了一個應用沦泌,那么下一次開機啟動時,該應用也處于打開狀態(tài)辛掠。而正常的關機-開機流程赦肃,該應用是不會打開的。
Linux內(nèi)核代碼聲明如下,位于kernel/power/suspend.c
在新版內(nèi)核中公浪,進程freeze的功能被單獨抽離出來作為一個電源狀態(tài),該狀態(tài)僅僅是凍結進程船侧,并不會使系統(tǒng)進入低功耗狀態(tài)(如切斷CPU時鐘源欠气、關閉外設供電等)。
相關宏定義位于:linux/include/linux/suspend.h
其中狀態(tài)4就是STD镜撩,所謂的休眠狀態(tài)(Hibernation)
小結:
至此预柒,我們可以知道队塘,睡眠與休眠是2個不同的概念,睡眠屬于STR宜鸯,而休眠屬于STD憔古,切勿混為一談。
網(wǎng)上也有很多關于“Android休眠”的文章淋袖,事實上鸿市,Android手機壓根兒就不支持休眠模式。
查看Linux支持的電源模式
#查看系統(tǒng)支持的電源模式
$ cat /sys/power/state
#休眠系統(tǒng)命令
$ sudo pm-hibernate
看來Ubuntu-17.0.4版本是不支持休眠功能了即碗,state當中并沒有disk焰情,執(zhí)行休眠命令也提示找不到。
在公司測試Ubuntu-16.0.4是支持休眠的剥懒,休眠時會將當前RAM中的數(shù)據(jù)保持至swap分區(qū)内舟,以供后續(xù)恢復。
查看Android支持的電源模式
這里我使用的是模擬器查看的初橘,真機也一樣验游,Android手機是不支持休眠模式的,休眠模式需要一塊與RAM大小一致存儲空間保檐,這在移動設備上可是個不小的開銷耕蝉。
Idle State
Android上的Idle狀態(tài)分為二類:Cpu Idle和Device Idle
Cpu Idle
Linux系統(tǒng)運行的基礎是基于進程調(diào)度,實際上內(nèi)核調(diào)度的線程(task)展东,內(nèi)核并不會區(qū)分線程與進程赔硫,都將他們當做一個線程(task)來處理;當所有的進程都沒事兒干的時候盐肃,系統(tǒng)就會啟用idle進程爪膊,使系統(tǒng)進入低功耗狀態(tài)(如關閉一些服務、模塊功能砸王,降低CPU工作頻率等)推盛,即idle狀態(tài),以達到省電的目的谦铃。
idle狀態(tài)又可以劃分為不同的層級耘成,以MTK的芯片為例,通常劃分為以下幾個狀態(tài):
狀態(tài) | 描述 |
---|---|
soidle(screen on idle) | 亮屏 Idle 模式驹闰,該模式下與正常工作狀態(tài)差別不大瘪菌,唯一的區(qū)別就cpu處于空閑狀態(tài) |
rgidle | 淺度 Idle 模式,cpu處于 WFI(wait for interrupt)嘹朗,屏幕熄滅师妙,同時關閉一些不需要的服務及模塊,注意此狀態(tài)cpu的時鐘源與RTC模塊是工作正常的屹培,此時是可以通過TimerTask的定時觸發(fā)激活系統(tǒng)的默穴,TimerTask依賴于CPU的RTC模塊怔檩,而Alarm則依賴于PMIC的RTC模塊 |
dpidle(deep idle) | 深度idle模式,該模式下cpu的時鐘源和hrtimer(高精度定時器模塊(RTC))被關閉蓄诽,所有進程(包括系統(tǒng)進程)被凍結薛训,即進入上文所述的睡眠狀態(tài) |
idle進程是由原始進程(pid=0)在初始化init進程(pid=1)之后演變而來,可以說是init進程的祖先仑氛,關于其詳細介紹可參考如下鏈接:
Linux Idle基礎
魅族內(nèi)核團隊:CPUIDLE 之低功耗定時器
Device Idle
Device Idle屬于Doze模式中概念乙埃,即指當手機屏幕熄屏、不充電调衰、靜置不動,有網(wǎng)友分析了源碼膊爪,指出6.0手機需要靜置1時4分30秒才能進入Doze模式。
Doze模式的限制 |
---|
網(wǎng)絡接入被暫停 |
系統(tǒng)忽略wake locks |
標準的AlarmManager alarms(包括setExact()和setWindow())被延緩到下一個maintenance window |
如果你需要在Doze狀態(tài)下啟動設置的alarms嚎莉,使用setAndAllowWhileIdle()或者setExactAndAllowWhileIdle()米酬。當有setAlarmClock()的alarms啟動時,系統(tǒng)會短暫退出Doze模式 |
系統(tǒng)不會掃描Wi-Fi |
系統(tǒng)不允許sync adapters運行 |
系統(tǒng)不允許JobScheduler運行 |
結合上文分析的cpu idle不難發(fā)現(xiàn)Doze模式中的idle狀態(tài)在概念屬于淺idle狀態(tài)趋箩,只是關閉了一些特定服務和模塊赃额,并非立即進入睡眠,當然這個過程當中依舊有可能滿足睡眠條件而進入睡眠狀態(tài)叫确,至于如何進入請參考下文【睡眠觸發(fā)入口】一節(jié)跳芳。
Android電源管理框架
Android采用linux內(nèi)核,所以電源狀態(tài)整體上是與linux操作系統(tǒng)相同竹勉,下圖是Android的電源管理框架:
WakeLock
喚醒鎖飞盆,一種鎖機制,用于阻止系統(tǒng)進入睡眠狀態(tài)次乓,只要有應用獲取到改鎖吓歇,那么系統(tǒng)就無法進入睡眠狀態(tài)。
該機制起初是早期Android為Linux內(nèi)核打得一個補丁票腰,并想合入到linux內(nèi)核城看,但被Linux社區(qū)拒絕,后續(xù)Linux內(nèi)核引入自己的Wakelock機制,Android系統(tǒng)也使用的是linux的Wakelock機制杏慰,所以該機制并非Android特有的機制测柠。
Android系統(tǒng)提供了兩種類型的鎖,每一個類型又可分為超時鎖與普通鎖缘滥,超時鎖轰胁,超時會自動釋放,而普通鎖則必需要手動釋放:
類型 | 描述 |
---|---|
WAKE_LOCK_SUSPEND | 阻止系統(tǒng)進入睡眠狀態(tài)(STR) |
WAKE_LOCK_IDLE | 阻止系統(tǒng)從idle進程進入那些具有較大中斷時延朝扼、禁用了較多中斷源的低功耗狀態(tài)(睡眠除外)软吐,持有該類型的鎖,不影響系統(tǒng)進入睡眠狀態(tài)吟税。自Android API-17(對應android linux內(nèi)核版本3.4)移除了該類型的喚醒鎖凹耙。 |
中斷時延:計算機接收到中斷信號到操作系統(tǒng)作出響應,并完成轉(zhuǎn)入中斷服務程序(ISR)的時間肠仪。
內(nèi)核當中關于WakeLock的主要源碼位于:
kernel_common/include/linux/wakelock.h
kernel_common/kernel/power/wakelock.c
應用層提供的鎖類型如下肖抱,這些鎖都需要手動釋放:
FLAG | CPU | 屏幕 | 鍵盤 |
---|---|---|---|
PARTIAL_WAKE_LOCK | 開啟 | 關閉 | 關閉 |
SCREEN_DIM_WAKE_LOCK | 開啟 | 變暗 | 關閉 |
SCREEN_BRIGHT_WAKE_LOCK | 開啟 | 變亮 | 關閉 |
FULL_WAKE_LOCK | 開啟 | 變亮 | 變亮 |
鎖的釋放
Linux3.4內(nèi)核中摒棄了之前的wakelock機制,引入wakeup source機制來進行睡眠管理异旧,為了保證上層接口不變意述,Android的Linux內(nèi)核便將wakeup source包裝成wakelock,WakeLock的數(shù)據(jù)結構如下:
當我們應用層釋放鎖之后吮蛹,它并不會馬上消失荤崇。wakelock分為激活和非激活狀態(tài),非激活狀態(tài)300S之內(nèi)潮针,無人在申請wakelock术荤,那么它將從紅黑二叉樹,LRU鏈表當中刪除每篷,如此便可復用鎖瓣戚,節(jié)省系統(tǒng)開銷。
睡眠觸發(fā)入口
在wakelock中焦读,有3個地方可以讓系統(tǒng)從early_suspend進入suspend狀態(tài)子库。
- wake_unlock,系統(tǒng)每釋放一個鎖矗晃,就會檢查是否還存其他激活的wakelock仑嗅,若不存在則執(zhí)行Linux的標準suspend流程進入睡眠狀態(tài)
- 在超時鎖的超時回調(diào)函數(shù),判斷是否存在其他激活的wakelock张症,若不存在仓技,則進入睡眠狀態(tài)
-
autosleep機制,android 4.1引入該機制吠冤,亮屏時會向autosleep節(jié)點寫入off浑彰,熄屏則會寫入mem。Android一滅屏拯辙,就會嘗試進入睡眠郭变,失敗之后系統(tǒng)處于idle進程超過一定時間,則又嘗試進入睡眠涯保,判斷標準同上诉濒,若存在wakelock則進入失敗
關于autosleep機制的內(nèi)核源碼分析,可以參考如下文章:
Android autosleep機制
Early Suspend
預掛起機制是Android特有的掛起機制夕春, 這個機制作用是關閉一些與顯示相關的外設未荒,比如LCD背光、重力感應器及志、 觸摸屏片排,但是其他外設如WIFI寨腔、藍牙等模塊等并未關閉。
此時率寡,系統(tǒng)依舊可以處理事件迫卢,如音樂播放軟件,息屏后依舊能播放音樂冶共。
需要注意的是Early Suspend機制與WakeLock機制相互獨立乾蛤,就算有應用持有wakelock鎖,系統(tǒng)依舊可以通過Early Suspend機制關閉與顯示相關的外設捅僵。
注意:
Android 4.4起家卖,也就是引入ART的版本,摒棄了early suspend機制庙楚,改用了fb event通知機制上荡,即后續(xù)版本只有suspend、resume以及runtime suspend醋奠、runtime resume榛臼。
Late Resume
遲喚醒機制,用于喚醒預掛起的設備
睡眠狀態(tài)轉(zhuǎn)換
一般情況下窜司,當我們息屏后沛善,系統(tǒng)將先通過Early Suspend機制進入Idle狀態(tài),如果滿足進入睡眠的條件(沒有進程持有喚醒鎖)則會通過Linux的Suspend機制進入Sleep(睡眠)狀態(tài)塞祈。
內(nèi)核源碼流程分析可參考如下文章:
源碼位于kernel_common/kernel/power/main.c:
Android中休眠與喚醒之wake_lock, early_suspend, late_resume
看到這兒金刁,不知你是否疑問,既然系統(tǒng)睡眠了议薪,CPU斷電不執(zhí)行指令了尤蛮,為何我們定的Alarm會生效以及能接收到來電?
手機來電與Alarm為何能喚醒系統(tǒng)
原來Android在硬件架構上將處理器分為二類:Application Processor(AP)和Baseband Processor(BP)斯议,AP是ARM架構的處理器产捞,用于運行Linux+Android系統(tǒng),耗電量高哼御;BP用于運行實時操作系統(tǒng)(RTOS)坯临,用于處理手機通信,耗電量低恋昼。
當AP進入睡眠看靠,有來電時,Modem(調(diào)制解調(diào)器)將喚醒AP液肌;而我們平時所用的Alarm在硬件上則是依賴PMIC(電源管理芯片)中的RTC模塊挟炬,所以即使AP斷電進入睡眠,我們定的鬧鐘依舊會生效。
若想更深入的了解谤祖,則可參考Android RIL機制相關的文章婿滓。
總結
待機、睡眠與休眠的區(qū)別
實際上待機(standby)與睡眠(mem)屬于不同模式泊脐,但現(xiàn)在大多操作系統(tǒng)都不支持待機模式了空幻,我們也習慣將待機等同于睡眠,睡眠屬于STR容客,休眠屬于STD,Android手機不支持休眠T加簟K跆簟!Android開發(fā)者官網(wǎng)當中提到“idle state”闲延,該如何理解谜酒,這個狀態(tài)會對設備及我們的程序造成何種影響
所謂的idle狀態(tài)进鸠,就是指系統(tǒng)進入某個低功耗狀態(tài),以MTK為例芥丧,常見的狀態(tài)有soidle、rgidle以及dpidle坊罢。rgidle只是限制我們程序使用某些模塊续担,如Doze模式中不能訪問網(wǎng)絡;而dpidle則會凍結所有進程活孩,系統(tǒng)進入睡眠物遇。進入Doze模式中的idle狀態(tài),我們的程序還能運行嗎憾儒?
Doze模式中的idle概念上屬于rgidle狀態(tài)询兴,此時我們的程序是能運行的,只是不能訪問網(wǎng)絡等起趾,但是在這個過程中诗舰,系統(tǒng)可能會滿足進入睡眠條件,凍結所有進程训裆,這樣我們的程序就不會得到執(zhí)行眶根。
可以自己寫個死循環(huán)的線程(普通線程,非looper線程)缭保,強制手機進入Doze的idle模式汛闸,你會發(fā)現(xiàn)你的程序依舊在執(zhí)行,但是靜置在哪兒一段時間后艺骂,你會發(fā)現(xiàn)你的線程被凍結诸老,不會執(zhí)行,當你點亮屏幕,你的線程又會繼續(xù)工作别伏。手機睡眠之后蹄衷,為何我們寫Alarm程序、來電顯示程序依舊會生效厘肮?
Android在硬件架構上將處理器分為AP與BP愧口,應用程序運行與AP之中,睡眠只是將AP斷電类茂,BP(Modem)不會斷電耍属,當有來電時,BP將會喚醒AP巩检。
Alarm在硬件上依賴的是Modem中的PMIC的RTC模塊厚骗,而不是AP中的RTC模塊,當定時器觸發(fā)時兢哭,可以喚醒AP领舰,使我們的Alarm程序依舊會得到執(zhí)行
參考文章
維基百科ACPI
Linux電源管理子系統(tǒng)專題
Android wacklock獲取、釋放流程分析