1 什么是進程凍結
進程凍結技術(freezing of tasks)是指在系統hibernate或者suspend的時候,將用戶進程和部分內核線程置於“可控”的暫停狀態。
2 為什么需要凍結技術
假設沒有凍結技術,進程可以在任意可調度的點暫停,而且直到cpu_down才會暫停並遷移。這會給系統帶來很多問題:
(1)有可能破壞文件系統。在系統創建hibernate image到cpu down之間,如果有進程還在修改文件系統的內容,這將會導致系統恢復之后無法完全恢復文件系統;
(2)有可能導致創建hibernation image失敗。創建hibernation image需要足夠的內存空間,但是在這期間如果還有進程在申請內存,就可能導致創建失敗;
(3)有可能干擾設備的suspend和resume。在cpu down之前,device suspend期間,如果進程還在訪問設備,尤其是訪問競爭資源,就有可能引起設備suspend異常;
(4)有可能導致進程感知系統休眠。系統休眠的理想狀態是所有任務對休眠過程無感知,睡醒之后全部自動恢復工作,但是有些進程,比如某個進程需要所有cpu online才能正常工作,如果進程不凍結,那么在休眠過程中將會工作異常。
3 代碼實現框架
凍結的對象是內核中可以被調度執行的實體,包括用戶進程、內核線程和work_queue。用戶進程默認是可以被凍結的,借用信號處理機制實現;內核線程和work_queue默認是不能被凍結的,少數內核線程和work_queue在創建時指定了freezable標志,這些任務需要對freeze狀態進行判斷,當系統進入freezing時,主動暫停運行。
kernel threads可以通過調用kthread_freezable_should_stop來判斷freezing狀態,並主動調用__refrigerator進入凍結;work_queue通過判斷max_active屬性,如果max_active=0,則不能入隊新的work,所有work延后執行。
標記系統freeze狀態的有三個重要的全局變量:pm_freezing、system_freezing_cnt和pm_nosig_freezing,如果全為0,表示系統未進入凍結;system_freezing_cnt>0表示系統進入凍結,pm_freezing=true表示凍結用戶進程,pm_nosig_freezing=true表示凍結內核線程和workqueue。它們會在freeze_processes和freeze_kernel_threads中置位,在thaw_processes和thaw_kernel_threads中清零。
fake_signal_wake_up函數巧妙的利用了信號處理機制,只設置任務的TIF_SIGPENDING位,但不傳遞任何信號,然后喚醒任務;這樣任務在返回用戶態時會進入信號處理流程,檢查系統的freeze狀態,並做相應處理。
任務主動調用try_to_freeze的代碼如下:
- static inline bool try_to_freeze_unsafe(void)
- {
- if (likely(!freezing(current))) //檢查系統是否處於freezing狀態
- return false;
- return __refrigerator(false); //主動進入凍結
- }
- static inline bool freezing(struct task_struct *p)
- {
- if (likely(!atomic_read(&system_freezing_cnt))) //系統總體進入freezing
- return false;
- return freezing_slow_path(p);
- }
- bool freezing_slow_path(struct task_struct *p)
- {
- if (p->flags & PF_NOFREEZE) //當前進程是否允許凍結
- return false;
- if (pm_nosig_freezing || cgroup_freezing(p)) //系統凍結kernel threads
- return true;
- if (pm_freezing && !(p->flags & PF_KTHREAD)) //系統凍結用戶進程
- return true;
- return false;
- }
進入凍結狀態直到恢復的主要函數:
bool __refrigerator(bool check_kthr_stop)
- {
- ...
- for (;;) {
- set_current_state(TASK_UNINTERRUPTIBLE); //設置進程為UNINTERRUPTIBLE狀態
- spin_lock_irq(&freezer_lock);
- current->flags |= PF_FROZEN; //設置已凍結狀態
- if (!freezing(current) ||
- (check_kthr_stop && kthread_should_stop())) //判斷系統是否還處於凍結
- current->flags &= ~PF_FROZEN; //如果系統已解凍,則取消凍結狀態
- spin_unlock_irq(&freezer_lock);
- if (!(current->flags & PF_FROZEN)) //如果已取消凍結,跳出循環,恢復執行
- break;
- was_frozen = true;
- schedule();
- }
- ......
- }
4 參考文獻
(1) http://www.wowotech.net/linux_kenrel/suspend_and_resume.html
(2) http://www.wowotech.net/linux_kenrel/std_str_func.html
(3) kenrel document: freezing-of-tasks.txt