一、簡介
Android系統中定義了幾種低功耗狀態:earlysuspend、suspend、hibernation.
1) earlysuspend: 是一種低功耗的狀態,某些設備可以選擇進入某種功耗較低的狀態,比如 LCD可以降低亮度或滅掉;
2) suspend: 是指除電源管理以外的其他外圍模塊以及cpu均不工作,只有內存保持自刷新的狀態;
3) hibernation是指所有內存鏡像都被寫入磁盤中,然後系統關機,恢復後系統將能恢復到「關機」之前的狀態。是最徹底的低功耗模式,它把所有內存鏡像都寫入磁盤中,然後系統關機。該文件還在sysfs文件系統中創建了多個entry,分別是/sys/power/disk,/sys/power/resume和/sys/power/image_size,這樣用戶可以直接通過 sysfs 來控制系統進出hibernation狀態。這塊代碼跟標準Linux內核沒有什麼區別。
1) earlysuspend: 是一種低功耗的狀態,某些設備可以選擇進入某種功耗較低的狀態,比如 LCD可以降低亮度或滅掉;
2) suspend: 是指除電源管理以外的其他外圍模塊以及cpu均不工作,只有內存保持自刷新的狀態;
3) hibernation是指所有內存鏡像都被寫入磁盤中,然後系統關機,恢復後系統將能恢復到「關機」之前的狀態。是最徹底的低功耗模式,它把所有內存鏡像都寫入磁盤中,然後系統關機。該文件還在sysfs文件系統中創建了多個entry,分別是/sys/power/disk,/sys/power/resume和/sys/power/image_size,這樣用戶可以直接通過 sysfs 來控制系統進出hibernation狀態。這塊代碼跟標準Linux內核沒有什麼區別。
在打過android補丁的內核中, state_store()函數會走另外一條路,會進入到request_suspend_state()中, 這個文件在earlysuspend.c中. 這些功能都是android系統加的,後面會對earlysuspend和late resume 進行介紹。
二、用戶接口
電源管理內核層給應用層提供的接口就是sysfs 文件系統,所有的相關接口都通過sysfs實現。Android上層frameworks也是基於sysfs做了包裝,最終提供給Android java應用程序的是java類的形式。
Android系統會在sysfs裡面創建以entry:
/sys/power/state
/sys/power/wake_lock
/sys/power/wake_unlock
Android系統會在sysfs裡面創建以entry:
/sys/power/state
/sys/power/wake_lock
/sys/power/wake_unlock
echo mem > /sys/power/state或echo standby > /sys/power/state: 命令系統進入earlysuspend狀態,那些註冊了early suspend handler的驅動將依次進入各自的earlysuspend 狀態。
echo on > /sys/power/state: 將退出early suspend狀態
echo disk > /sys/power/state: 命令系統進入hibernation狀態
echo lockname > /sys/power/wake_lock: 加鎖「lockname」
echo lockname > /sys/power/wake_unlock: 解鎖「lockname」
上述是分別加鎖和解鎖的命令,一旦系統中所有wakelock被解鎖,系統就會進入suspend狀態,可見Linux中原本使系統suspend 的操作(echo mem > /sys/power/state 等)在Android被替換成使系統進入early suspend;而wake lock 機製成為用戶命令系統進入suspend狀態的唯一途徑。
echo lockname > /sys/power/wake_unlock: 解鎖「lockname」
上述是分別加鎖和解鎖的命令,一旦系統中所有wakelock被解鎖,系統就會進入suspend狀態,可見Linux中原本使系統suspend 的操作(echo mem > /sys/power/state 等)在Android被替換成使系統進入early suspend;而wake lock 機製成為用戶命令系統進入suspend狀態的唯一途徑。
三、Android 休眠(suspend)
1. 相關文件 • kernel/kernel/power/main.c
• kernel/kernel/power/earlysuspend.c
• kernel/kernel/power/wakelock.c
• kernel/kernel/power/earlysuspend.c
• kernel/kernel/power/wakelock.c
2. 特性介紹
1) Early Suspend Early suspend 是android 引進的一種機制,這種機制在上游備受爭議,這裡不做評論。 這個機製作用是在關閉顯示的時候,一些和顯示有關的設備,比如LCD背光、重力感應器、 觸摸屏都會關掉,但是系統可能還是在運行狀態(這時候還有wake lock)進行任務的處理,例如在掃瞄 SD卡上的文件等。 在嵌入式設備中,背光是一個很大的電源消耗,所以android會加入這樣一種機制。
1) Early Suspend Early suspend 是android 引進的一種機制,這種機制在上游備受爭議,這裡不做評論。 這個機製作用是在關閉顯示的時候,一些和顯示有關的設備,比如LCD背光、重力感應器、 觸摸屏都會關掉,但是系統可能還是在運行狀態(這時候還有wake lock)進行任務的處理,例如在掃瞄 SD卡上的文件等。 在嵌入式設備中,背光是一個很大的電源消耗,所以android會加入這樣一種機制。
2) Late Resume Late Resume 是和suspend 配套的一種機制,是在內核喚醒完畢開始執行的。主要就是喚醒在Early Suspend時休眠的設備。
3) Wake Lock wake_lock 在Android的電源管理系統中扮演一個核心的角色。wake_lock是一種鎖的機制,只要有人拿著這個鎖,系統就無法進入休眠,可以被用戶態程序和內核獲得。這個鎖可以是有超時的或者是沒有超時的,超時的鎖會在超時以後自動解鎖。如果沒有鎖了或者超時了,內核就會啟動休眠的那套機制來進入休眠。
3. Android Suspend main.c文件是整個框架的入口。用戶可以通過讀寫sys文件/sys/power/state實現控制系統進入低功耗狀態。用戶對於/sys/power/state的讀寫會調用到main.c中的state_store(),用戶可以寫入const char * const pm_states[] 中定義的字符串, 比如「on」,「mem」,「standby」,「disk」。
state_store()首先判斷用戶寫入的是否是「disk」字符串,如果是則調用hibernate()函數命令系統進入hibernation狀態。如果是其他字符串則調用request_suspend_state()(如果定義 CONFIG_EARLYSUSPEND)或者調用enter_state()(如果未定義CONFIG_EARLYSUSPEND)。 request_suspend_state()函數是android相對標準linux改動的地方,它實現在earlysuspend.c中。在標準linux內核中,用戶通過 sysfs 寫入「mem」和「standby」時,會直接調用enter_state()進入suspend模式,但在android中則會調用request_suspend_state()函數進入early suspend狀態。request_suspend_state()函數代碼如下:
state_store()首先判斷用戶寫入的是否是「disk」字符串,如果是則調用hibernate()函數命令系統進入hibernation狀態。如果是其他字符串則調用request_suspend_state()(如果定義 CONFIG_EARLYSUSPEND)或者調用enter_state()(如果未定義CONFIG_EARLYSUSPEND)。 request_suspend_state()函數是android相對標準linux改動的地方,它實現在earlysuspend.c中。在標準linux內核中,用戶通過 sysfs 寫入「mem」和「standby」時,會直接調用enter_state()進入suspend模式,但在android中則會調用request_suspend_state()函數進入early suspend狀態。request_suspend_state()函數代碼如下:
4. Early Suspend 在early_suspend()函數中,首先會檢查現在請求的狀態還是否是suspend, 來防止suspend的請求會在這個時候取消掉(因為這個時候用戶進程還在運行),如果需要退出,就簡單的退出了。如果沒有, 這個函數就會把early_suspend_handlers中註冊的一系列的回調(通過register_early_suspend註冊)都調用一次,然後同步文件系統, 然後放棄掉main_wake_lock, 這個wake lock是一個沒有超時的鎖,如果這個鎖不釋放,那麼系統就無法進入休眠。
註:fbearlysuspend.c和consoleearlysuspend.c這兩個文件實現了針對lcd framebuffer的earlysuspend支持和console的earlysuspend支持。實際上這兩個文件就是利用上面earlysuspend.c提供的接口註冊了針對framebuffer和console的early suspend handler,並提供相應的handler函數。
5. Late Resume
當所有的喚醒已經結束以後,用戶進程都已經開始運行了,喚醒通常會是以下的幾種原因:
• 來電
如果是來電,那麼Modem會通過發送命令給rild來讓rild通知WindowManager有來電響應,這樣就會遠程調用PowerManagerService來寫"on" 到 /sys/power/state 來執行late resume的設備,比如點亮屏幕等。
如果是來電,那麼Modem會通過發送命令給rild來讓rild通知WindowManager有來電響應,這樣就會遠程調用PowerManagerService來寫"on" 到 /sys/power/state 來執行late resume的設備,比如點亮屏幕等。
• 用戶按鍵
用戶按鍵事件會送到WindowManager中,WindowManager會處理這些按鍵事件,按鍵分為幾種情況,如果按鍵不是喚醒鍵(能夠喚醒系統的按鍵) 那麼WindowManager會主動放棄wakeLock來使系統再次進入休眠,如果按鍵是喚醒鍵,那麼WindowManger就會調用PowerManagerService中的接口來執行Late Resume。
Late Resume 會依次喚醒前面調用了Early Suspend的設備。
6. Wake Lock wake_lock防止正在運行的系統進入suspend或其它低功耗狀態。
Android改動較大的另一處是增加了wakelock機制。實現在wakelock.c和userwakelock.c中。wakelock可以阻止處於正常運行(active)或者空閒(idle)狀態的系統進入睡眠等低功耗狀態。直到所持有的wakelock全部被釋放,系統才能進入睡眠等低功耗的狀態。
我們接下來看一看wake lock的機制是怎麼運行和起作用的,主要關注 wakelock.c(wake_lock)文件就可以了。
1) wake lock 有加鎖和解鎖兩種狀態,加鎖的方式有兩種:
• 第一種是永久的鎖住,這樣的鎖除非顯示的放開,是不會解鎖的,所以這種鎖的使用是非常小心的。
• 第二種是超時鎖,這種鎖會鎖定系統喚醒一段時間,如果這個時間過去了,這個鎖會自動解除。
• 第一種是永久的鎖住,這樣的鎖除非顯示的放開,是不會解鎖的,所以這種鎖的使用是非常小心的。
• 第二種是超時鎖,這種鎖會鎖定系統喚醒一段時間,如果這個時間過去了,這個鎖會自動解除。
2) 鎖有兩種類型:
• WAKE_LOCK_SUSPEND:這種鎖會防止系統進入睡眠(suspend)。
• WAKE_LOCK_IDLE:這種鎖不會影響系統的休眠,用於阻止系統在持有鎖的過程中進入低功耗狀態。即直到wake_lock被釋放,系統才會從idle狀態進入低功耗狀態,此低功耗狀態將使中斷延遲或禁用一組中斷。
• WAKE_LOCK_SUSPEND:這種鎖會防止系統進入睡眠(suspend)。
• WAKE_LOCK_IDLE:這種鎖不會影響系統的休眠,用於阻止系統在持有鎖的過程中進入低功耗狀態。即直到wake_lock被釋放,系統才會從idle狀態進入低功耗狀態,此低功耗狀態將使中斷延遲或禁用一組中斷。
3) 在wake lock中, 會有3個地方讓系統直接開始suspend(), 分別是:
• 在wake_unlock()中, 如果發現解鎖以後沒有任何其他的wake lock了,就開始休眠
• 在定時器都到時間以後,定時器的回調函數會查看是否有其他的wake lock,如果沒有,就在這裡讓系統進入睡眠。
• 在wake_lock() 中,對一個wake lock加鎖以後,會再次檢查一下有沒有鎖, 我想這裡的檢查是沒有必要的, 更好的方法是使加鎖的這個操作原子化,而不是繁冗的檢查,而且這樣的檢查也有可能漏掉。
• 在wake_unlock()中, 如果發現解鎖以後沒有任何其他的wake lock了,就開始休眠
• 在定時器都到時間以後,定時器的回調函數會查看是否有其他的wake lock,如果沒有,就在這裡讓系統進入睡眠。
• 在wake_lock() 中,對一個wake lock加鎖以後,會再次檢查一下有沒有鎖, 我想這裡的檢查是沒有必要的, 更好的方法是使加鎖的這個操作原子化,而不是繁冗的檢查,而且這樣的檢查也有可能漏掉。
7. Suspend
當wake_lock 運行 suspend()以後, 在wakelock.c的suspend()函數會被調用,這個函數首先sync文件系統,然後調用pm_suspend(request_suspend_state),接下來pm_suspend()就會調用enter_state()來進入Linux的休眠流程...
8. Android於標準Linux休眠的區別
pm_suspend() 雖然用enter_state()來進入標準的Linux休眠流程,但是還是有一些區別:
當進入凍結進程的時候,android首先會檢查有沒有wake lock,如果沒有,才會停止這些進程,因為在開始suspend和凍結進程期間有可能有人申請了wake lock,如果是這樣,凍結進程會被中斷。
在suspend_late()中,會最後檢查一次有沒有wake lock,這有可能是某種快速申請wake lock,並且快速釋放這個鎖的進程導致的,如果有這種情況, 這裡會返回錯誤, 整個suspend就會全部放棄。如果pm_suspend()成功了,LOG的輸出可以通過在kernel cmd裡面增加 "no_console_suspend" 來看到suspend和resume過程中的log輸出。
沒有留言:
張貼留言