Android待機的suspend_sys_sync_queue分析
Android的4.0在待機機制上和之前版本大同小異,也可以說是機制相對完善並沒多大的問題反饋出來。不過有個細節的地方,改動幅度較大,來看看
在linux待機機制中,開始待機的時候會調用sys_sync函數,sys_sync系統調用被用戶空間函數調用,
用來將緩存中的數據寫入塊設備,sys_sync系統調用將buffer、inode和super在緩存中的數據寫入設備。
此函數的介紹參看博文鏈接http://blog.chinaunix.net/uid-24237502-id-106067.html。
sys_sync函數執行時間長度依文件系統而定,長至上百毫秒,也有若干毫秒。函數執行的必要性如何呢?
如果有種需求,在很短時間(毫秒級)內需要連續進出待機,sys_sync函數是否需要每次都執行一遍呢?
如果只第一次執行,會有什麼隱患問題呢?
由於待機請求有自動超時待機和按鍵進入待機,自動進入待機問題不大,基本上是鬧鐘或者計時時間發起事件,
而按鍵進入待機不同,按鍵時間貫穿kernel到android上層,涉及節點處理,文件系統操作,
所以對同步設備節點數據sys_sync就有需求,可以看出來,android4.0在對sys_sync的調用修改上,也是考慮到按鍵進入待機的隱藏問題。
那到底做了哪些修改呢?
suspend_sys_sync_queue有三個地方在調用
Earlysuspend.c
(kernel\power)
Suspend.c
(kernel\power):
Wakelock.c
(kernel\power):
定義是在Wakelock.c (kernel\power)中
這個函數實現執行隊列中suspend_sys_sync_work
void suspend_sys_sync_queue(void)
{
int ret;
spin_lock(&suspend_sys_sync_lock);
ret = queue_work(suspend_sys_sync_work_queue, &suspend_sys_sync_work);
if (ret)
suspend_sys_sync_count++;
spin_unlock(&suspend_sys_sync_lock);
}
suspend_sys_sync_work_queue =
create_singlethread_workqueue("suspend_sys_sync");
static void suspend_sys_sync(struct work_struct *work)
{
if (debug_mask & DEBUG_SUSPEND)
pr_info("PM: Syncing filesystems...\n");
sys_sync();
if (debug_mask & DEBUG_SUSPEND)
pr_info("sync done.\n");
spin_lock(&suspend_sys_sync_lock);
suspend_sys_sync_count--;
spin_unlock(&suspend_sys_sync_lock);
}
static DECLARE_WORK(suspend_sys_sync_work, suspend_sys_sync);
所以調用suspend_sys_sync_queue,其實最後還是調用sys_sync,只是增加對sys_sync執行次數的計數,以及隊列方式執行並非立即執行。
隊列方式執行,是對執行的時間上有要求,那麼它到底需要等待多少時間呢?相對什麼等待呢?
待機,對系統而言,就是凍結,退出待機時候,對系統而言相當於什麼事情都沒發生。
標準linux待機在待機的開始階段,就會凍結所有的進程和任務,在喚醒時候恢復它們。
由於進程在kernel空間處理的複雜度,linux在凍結進程和任務的時候,不支持進程還跑在kernel空間,否則就是abort處理。
問題就出來了,最後一次按鍵進入待機,按鍵時間從鍵值採集、鍵值上報、上層對上報值的輪詢、對按鍵事件分發、對按鍵時間的處理等,
都需要一定時間,如果在進程未能較快回到用戶空間,freeze_processes的時候,如下
printk("Freezing user space processes ... ");
error = try_to_freeze_tasks(true);
if (error)
goto Exit;
printk("done.\n");
在freezing user space processes會abort。
補充一下說明:這裏針對的不全是最後一次按鍵,我們知道android爲了增加用戶體驗,在超時進入待機時候,會啓動一個5秒的定時鬧鐘,
從而阻止5秒內真正進入待機,在用戶體驗上,就是屏幕暗下來的5秒內,只要用戶及時點亮屏幕,系統就沒真正待機,不會有鎖屏界面。
開發員在此過程中會看到freezing user space processes abort,是因爲系統啓動了alarm,從而推出待機,系統恢復。
這裏增加了一個timer,用於等待sys_sync執行完畢
error = suspend_sys_sync_wait();
if (error)
goto Exit;
int suspend_sys_sync_wait(void)
{
suspend_sys_sync_abort = false;
if (suspend_sys_sync_count != 0) {
mod_timer(&suspend_sys_sync_timer, jiffies +
SUSPEND_SYS_SYNC_TIMEOUT);
wait_for_completion(&suspend_sys_sync_comp);
}
if (suspend_sys_sync_abort) {
pr_info("suspend aborted....while waiting for sys_sync\n");
return -EAGAIN;
}
return 0;
}
static bool suspend_sys_sync_abort;
static void suspend_sys_sync_handler(unsigned long);
static DEFINE_TIMER(suspend_sys_sync_timer, suspend_sys_sync_handler, 0, 0);
/* value should be less then half of input event wake lock timeout value
* which is currently set to 5*HZ (see drivers/input/evdev.c)
*/
#define SUSPEND_SYS_SYNC_TIMEOUT (HZ/4)
static void suspend_sys_sync_handler(unsigned long arg)
{
if (suspend_sys_sync_count == 0) {
complete(&suspend_sys_sync_comp);
} else if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
suspend_sys_sync_abort = true;
complete(&suspend_sys_sync_comp);
} else {
mod_timer(&suspend_sys_sync_timer, jiffies +
SUSPEND_SYS_SYNC_TIMEOUT);
}
}
等待的時間should be less then half of input event wake lock timeout value,
evdev事件會超時50ms,這裏的timeout需要小於25ms,此處設置爲2.5ms。
對timeout的要求,應該是爲了能夠更快地在evdev超時後執行timer的handler。
以上這些代碼,都可以追蹤CONFIG_SUSPEND_SYNC_WORKQUEUE宏定義。