若是你也有以上疑問,那麼本文會對你解開疑惑有必定的幫助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程序依舊會獲得執行