進程的調度時機與進程的切換
操作系統原理中介紹了大量進程調度算法,這些算法從實現的角度看僅僅是從運行隊列中選擇一個新進程,選擇的過程中運用了不同的策略而已。
對於理解操作系統的工作機制,反而是進程的調度時機與進程的切換機制更為關鍵。
進程調度的時機
-
中斷處理過程(包括時鍾中斷、I/O中斷、系統調用和異常)中,直接調用schedule(),或者返回用戶態時根據need_resched標記調用schedule();
-
內核線程可以直接調用schedule()進行進程切換,也可以在中斷處理過程中進行調度,也就是說內核線程作為一類的特殊的進程可以主動調度,也可以被動調度;
-
用戶態進程無法實現主動調度,僅能通過陷入內核態后的某個時機點進行調度,即在中斷處理過程中進行調度。
進程的切換
-
為了控制進程的執行,內核必須有能力掛起正在CPU上執行的進程,並恢復以前掛起的某個進程的執行,這叫做進程切換、任務切換、上下文切換;
-
掛起正在CPU上執行的進程,與中斷時保存現場是不同的,中斷前后是在同一個進程上下文中,只是由用戶態轉向內核態執行;
-
進程上下文包含了進程執行需要的所有信息
-
用戶地址空間:包括程序代碼,數據,用戶堆棧等
-
控制信息:進程描述符,內核堆棧等
-
硬件上下文(注意中斷也要保存硬件上下文只是保存的方法不同)
-
schedule()函數選擇一個新的進程來運行,並調用context_switch進行上下文的切換,這個宏調用switch_to來進行關鍵上下文切換
-
next = pick_next_task(rq, prev);//進程調度算法都封裝這個函數內部
-
context_switch(rq, prev, next);//進程上下文切換
-
switch_to利用了prev和next兩個參數:prev指向當前進程,next指向被調度的進程
Linux系統的一般執行過程
最一般的情況:正在運行的用戶態進程X切換到運行用戶態進程Y的過程
-
正在運行的用戶態進程X
-
發生中斷——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack).
-
SAVE_ALL //保存現場
-
中斷處理過程中或中斷返回前調用了schedule(),其中的switch_to做了關鍵的進程上下文切換
-
標號1之后開始運行用戶態進程Y(這里Y曾經通過以上步驟被切換出去過因此可以從標號1繼續執行)
-
restore_all //恢復現場
-
iret - pop cs:eip/ss:esp/eflags from kernel stack
-
繼續運行用戶態進程Y
幾種特殊情況
- 通過中斷處理過程中的調度時機,用戶態進程與內核線程之間互相切換和內核線程之間互相切換,與最一般的情況非常類似,只是內核線程運行過程中發生中斷沒有進程用戶態和內核態的轉換;
- 內核線程主動調用schedule(),只有進程上下文的切換,沒有發生中斷上下文的切換,與最一般的情況略簡略;
- 創建子進程的系統調用在子進程中的執行起點及返回用戶態,如fork;
- 加載一個新的可執行程序后返回到用戶態的情況,如execve;
Linux操作系統架構和系統執行過程
操作系統的基本概念
操作系統:任何計算機系統包含的一個基本的程序集合
- 內核(進程管理、進程調度、進程間通訊機制、內存管理、中斷異常處理、文件系統、I/O系統、網絡部分)
- 其他程序(函數庫、shell程序、系統程序等等)
操作系統的目的
- 與硬件交互,管理所有的硬件資源
- 為用戶程序(應用程序)提供一個良好的執行環境
典型的linux操作系統的結構
最簡單也是最復雜的操作ls
CPU執行指令
內存在執行指令過程中
實驗:使用gdb跟蹤分析schedule函數執行過程
遠程調試並設置斷點
調試運行
實驗總結:schedule()函數用來選擇一個新的進程來運行,並調用context_switch()進行上下文的切換,這個宏調用switch_to()來進行關鍵上下文切換,其中pick_next_task()函數封裝了進程調度算法