有關進程的知識在前面的博客中已經提到了,有不懂的地方請參考我前面的博客,今天我直接從進程調度和切換開始講。
Linux一個較大的優勢就是進程調度,因為Linux是一個多進程系統,它怎么進行進程調度直接影響這個系統的性能,而Linux系統的一個優勢就是它的系統在進程調度這里做的很好。
在講進程調度前,我們先來看下有關Linux知識(以下4張圖片摘自孟寧老師課件)。
圖1.Linux內核架構
圖2.Linux執行過程
圖3.CPU執行指令角度
圖4.從內存角度看待Linux系統執行
首先一起來看下Linux的進程的類型,一般將進程分為三種,一種為I/O消耗型進程,另一種是處理器消耗型進程,還有一種是混合型,也就是I/O消耗型進程和處理器消耗型進程混合在一起的。從他們的名字可以看出,這是以進程消耗資源的種類來進行分類的。
在Linux系統中,是按照什么規則來進行調度的呢?我們所知的有優先級調度,還有時間片調度。其中優先級指的是進程的優先級,而時間片則指的是進程所需要消耗的時間。
那么Linux系統中進程調度的過程到底是一個什么流程呢?主要是以下幾個方面
1.從schedule()函數開始,進行調度選擇
2.從CPU的值變化上,解讀switch_to宏執行分析
3.到堆棧發生切換位置,在切換堆棧前后,current_thread_info變化
4.再到地址空間發生切換,解釋地址空間的切換不會影響后續切換代碼的執行
5.Current宏代表的進程發生變化的源碼位置
6.任務狀態段中關于內核堆棧的信息發生變化的源碼位置
下面來詳細的講解一下各個環節
在Linux內核中,schedule()函數選擇一個新的進程來運行,并調用context_switch進行上下文的切換,這個宏調用switch_to來進行關鍵上下文切換。部分函數具體代碼如下,一個調度新進程,一個是進行上下文切換,還有相關堆棧信息的保存。
next=pick_next_task(rq,prev);//進程調度算法都封裝這個函數內部
context_switch(rq,prev,next);//進程上下文切換
switch_to利用了prev和next兩個參數:prev指向當前進程,next指向被調度的進程
Schedule:主要負責幫助系統選定下一個執行的進程
調度時機:
1.進程狀態轉換的時刻,進程終止、進程睡眠。
2.當前進程的時間片用完時。
3.設備驅動程序調用。
4.進程從中斷、異常及系統調用返回到用戶態時。
進程的從睡眠狀態到喚醒狀態,完成了一次進程的調度,中間有保存相應的進程信息,有相應的隊列進行保存。
進程調度中還有一個現象是搶占,就是優先級高的進程進行搶占低優先級的執行機會,用戶搶占發生在兩種情況下,一個是從系統調用返回用戶空間,另一個是從中斷處理程序返回用戶空間。
而內核搶占發生在:
1.當從中斷處理程序正在執行,且返回內核空間之前;
2.當內核代碼再一次具有可搶占性的時候;
3.如果內核中的任務顯式調用;
4.內核中的任務被阻塞。
上下文的切換也是進程調用中一個比較重要的問題,其中有一個context_switch()函數完成以下工作,switch_mm()——該函數負責把虛擬內存從上一個進程映射切換到新進程中。而switch_to()——負責從上一個進程的處理器狀態切換到新進程的處理器狀態。切換的過程包括保存、恢復棧信息和寄存器信息。
其中各種進程之間的調度又有很多方法,主要有先進先出、時間片輪轉等方式,這里就不具體分析,有興趣的可以自行查閱相應的調度方法。
下面來看下具體實驗過程:
圖5.相關指令操作,創建文件
圖6.文件內部修改處
圖7.文件內部修改處
圖8.調試啟動
圖9.設置斷點
圖10.查看context_switch處相關點代碼
圖11.中途的代碼調試過程
總結:從上面可以看出,Linux系統的進程切換的一般執行過程是這樣的,從進程X轉向進程Y的過程是這樣的。
1.正在運行的用戶態進程X。
2.發生中斷——savecs:eip/esp/eflags(current)tokernelstack,thenloadcs:eip(entryofaspecificISR)and
ss:esp(pointtokernelstack)。
3.SAVE_ALL//保存現場。
4.中斷處理過程中或中斷返回前調用了schedule(),其中的switch_to做了關鍵的進程上下文切換。
5.標號1之后開始運行用戶態進程Y(這里Y曾經通過以上步驟被切換出去過因此可以從標號1繼續執行)。
6.restore_all//恢復現場。
7.iret-popcs:eip/ss:esp/eflagsfromkernelstack//恢復。
8.繼續運行用戶態進程Y
好了,從上面可以看到,整個Linux系統的進程切換的執行流程就是這個樣子的!