2025-05-23

Android的4.0在待機機制上和之前版本大同小異,也可以說是機制相對完善並沒多大的問題反饋出來。不過有個細節的地方,改動幅度較大,來看看

在linux待機機制中,開始待機的時候會調用sys_sync函數,sys_sync系統調用被用戶空間函數調用,
用來將緩存中的數據寫入塊設備,sys_sync系統調用將buffer、inode和super在緩存中的數據寫入設備。
此函數的介紹參看博文鏈接/os/201204/126687.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
[csharp]
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); 

[csharp]
suspend_sys_sync_work_queue = 
    create_singlethread_workqueue("suspend_sys_sync"); 

[csharp]
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的時候,如下
[csharp]
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執行完畢
[csharp]
error = suspend_sys_sync_wait(); 
if (error) 
    goto Exit; 

[csharp]
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; 

[csharp]
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宏定義。

摘自  J.A.Y的專欄

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *