第八週學習總結——進程的切換和系統的通常執行過程

第八週學習總結——進程的切換和系統的通常執行過程

做者:劉浩晨
【原創做品轉載請註明出處】 《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000linux

1、進程切換的關鍵代碼switch_to的分析

1.進程調度與進程調度的時機分析

(1)爲何有多種進程調度算法——每一個進程對CPU、I/O等資源需求不同。算法

  • 第一種分類:shell

    I/O密集型(I/O-bound):頻繁的進行I/O,一般會花費不少時間等待I/O操做的完成
      CPU密集型(CPU-bound):計算密集型,須要大量的CPU時間進行運算
  • 第二種分類:架構

    批處理進程:沒必要與用戶交互,一般在後臺運行;沒必要很快響應典型的批處理程序:編譯程序,科學計算
      實時進程:有實時需求,不該被低優先級的進程阻塞;響應時間要短、要穩定;典型的實時進程:視頻、音配、機械控制。
      交互式進程:須要常常與用戶交互,所以要花不少時間等待用戶輸入操做;響應時間要快,平均延遲低於50~150ms;典型的交互式程序: shell,文本編輯程序,圖形應用程序等

(2)調度策略:一組規則,決定何時以怎樣的方式選擇一個新進程運行。函數

  • Linux的調度基於分時和優先級策略。
  • Linux的進程根據優先級(系統根據特定算法計算出來的一個值,表示把進程如何適當分配CPU)排隊。
  • Linux中進程的優先級是動態的——調度程序會根據進程的運行週期動態調整其優先級;較長時間未分配到CPU的進程,一般提高優先級,已經在CPU上運行了較長時間的進程,一般下降優先級。
  • 內核中的調度算法相關代碼使用了相似OOD中的策略模式。

(3)進程調度(schedule()函數實現)的時機:學習

  • 中斷處理過程(包括時鐘中斷、I/O中斷、系統調用和異常)中,直接調用schedule(),或者返回用戶態時根據need_resched標記調用schedule();
  • 內核線程(只有內核態沒有用戶態)能夠直接調用schedule()進行進程切換,也能夠在中斷處理過程當中進行調度,也就是說內核線程做爲一類的特殊的進程能夠主動調度,也能夠被動調度;
  • 用戶態進程沒法實現主動調度,僅能經過陷入內核態後的某個時機點進行調度,即在中斷處理過程當中進行調度。

注意:用戶態進程只能被動調度,內核線程是隻有內核態沒有用戶態的特殊進程。操作系統

2. 進程上下文切換相關代碼分析

  • 爲了控制進程的執行,內核必須有能力掛起正在CPU上執行的進程,並恢復之前掛起的某個進程的執行,這叫作進程切換、任務切換、上下文切換;線程

  • 掛起正在CPU上執行的進程,與中斷時保存現場是不一樣的,中斷先後是在同一個進程上下文中,只是由用戶態轉向內核態執行;3d

  • 進程上下文包含了進程執行須要的全部信息調試

    用戶地址空間:包括程序代碼,數據,用戶堆棧等
        控制信息:進程描述符,內核堆棧等
        硬件上下文(注意中斷也要保存硬件上下文只是保存的方法不一樣)
  • schedule()函數選擇一個新的進程來運行,並調用context_switch進行上下文的切換,這個宏調用switch_to來進行關鍵上下文切換

    next = pick_next_task(rq, prev);  //進程調度算法都封裝這個函數內部
      context_switch(rq, prev, next);  //進程上下文切換
      switch_to利用了prev和next兩個參數:prev指向當前進程,next指向被調度的進程。

    switch_to彙編代碼分析:

    #define switch_to(prev, next, last)                    
      do {                                 
    
             unsigned long ebx, ecx, edx, esi, edi;                
    
             asm volatile("pushfl\n\t"     
                "pushl %%ebp\n\t"        /* 把當前進程的堆棧基址壓棧  */ 
                "movl %%esp,%[prev_sp]\n\t"  /* 把當前進程的棧頂保存到thread.sp  */ 
                "movl %[next_sp],%%esp\n\t"  /* 這兩行完成內核堆棧切換,把下一進程棧頂放入esp  */ 
                "movl $1f,%[prev_ip]\n\t"    /* 保存當前進程的EIP,next_ip通常是$1f,對於新建立的子進程是ret_from_fork   */ 
                "pushl %[next_ip]\n\t"   /* 把下一個進程的起點EIP壓入堆棧   */    
                __switch_canary                   
                "jmp __switch_to\n"  /* 經過寄存器傳遞參數,返回1f  */ 
                "1:\t"               /* 認爲next進程開始執行,執行下一進程的第一條指令 */         
               "popl %%ebp\n\t"     /* restore EBP   */    
                  "popfl\n"         /* restore flags */  
                 /* output parameters 由於處於中斷上下文,在內核中 prev_sp是內核堆棧棧頂 */                
               : [prev_sp] "=m" (prev->thread.sp),//內核堆棧的棧頂     
                [prev_ip] "=m" (prev->thread.ip),  //[prev_ip]是標號        
                  "=a" (last),                        
                   /* clobbered output registers: */     
                  "=b" (ebx), "=c" (ecx), "=d" (edx),      
               "=S" (esi), "=D" (edi)                 
                 __switch_canary_oparam                              
               : [next_sp]  "m" (next->thread.sp), //下一個進程的內核堆棧的棧頂       
                  [next_ip]  "m" (next->thread.ip), //下一個進程執行的起點,通常是$1f,對於新建立的子進程是ret_from_fork            
                 /* regparm parameters for __switch_to(): */  
                 [prev]     "a" (prev),              
                 [next]     "d" (next)                 
                 __switch_canary_iparam                
                : /* reloaded segment registers */           
                 "memory");                  
      } while (0)

2、Linux系統的通常執行過程

1.Linux系統的通常執行過程分析

最通常的狀況:正在運行的用戶態進程X切換到運行用戶態進程Y的過程

(1)正在運行的用戶態進程X

(2)發生中斷——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).

(3)SAVE_ALL //保存現場

(4)中斷處理過程當中或中斷返回前調用了schedule(),其中的switch_to作了關鍵的進程上下文切換

(5)標號1以後開始運行用戶態進程Y(這裏Y曾經經過以上步驟被切換出去過所以能夠從標號1繼續執行)

(6)restore_all //恢復現場

(7)iret - pop cs:eip/ss:esp/eflags from kernel stack

(8)繼續運行用戶態進程Y

2.linux系統執行過程當中的幾個特殊狀況

  • 經過中斷處理過程當中的調度時機,用戶態進程與內核線程之間互相切換和內核線程之間互相切換,與最通常的狀況很是相似,只是內核線程運行過程當中發生中斷的時候沒有進程用戶態和內核態的轉換,cs不會變化;
  • 內核線程主動調用schedule(),只有進程上下文的切換,沒有發生中斷上下文的切換,也不須要從中斷中返回,與最通常的狀況相比更簡略;
  • 建立子進程的系統調用在子進程中的執行起點(如ret_from_fork,上文中也已經提到過)及返回用戶態,如fork();
    加載一個新的可執行程序後返回到用戶態的狀況,如execve,在新進程內部修改了中斷保存的信息

3.內核和舞女

  • 地址空間4G,3G以上只有內核態能夠訪問。
  • 內核就至關於出租車,進程招手就能夠進來完成內核態到用戶態的轉換;沒有進程就空轉。
  • 3G以上對全部進程是共享的,內核是各類中斷處理程序和內核線程的集合。

3、Linux系統架構和執行過程概覽

1.Linux操做系統架構概覽

(1)操做系統——任何計算機系統都包含一個基本的程序集合。

(2)操做系統有兩個目的:

  • 與硬件交互,管理全部的硬件資源;
  • 爲用戶程序(應用程序)提供一個良好的執行環境。

2.最簡單也是最複雜的操做——執行ls命令

3. 從CPU和內存的角度看Linux系統的執行

實驗——理解進程調度時機跟蹤分析進程調度與進程切換的過程!

1.根據之前所學步驟,配置好試驗環境:

2.進入gdb調試,設置斷點:


3.執行c,看到此時被凍結的內核運行到schedule處中斷:

4.單步執行到__schedule處,進入函數繼續單步執行到pick_next_task():

5.繼續執行c,到context_switch斷點處。list查看詳細代碼:

6.繼續單步執行可看到prepare_task_switch(),這個函數爲進程上下文切換作準備工做:

7.繼續單步執行可看到switch_to(),完成跟蹤:

理解總結:

根據這周所學課本上內容,本週主要理解Linux中進程調度與進程切換過程。進程調度是按必定的策略動態地把處理機分配給處於就緒隊列中的某一個進程,以使之執行。而進程切換是從正在運行的進程中收回處理器,而後再使待運行進程來佔用處理器。實質上就是把進程存放在處理器的寄存器中的中間數據找個地方存起來,從而把處理器的寄存器騰出來讓其餘進程使用。對於具體過程實驗中也有跟蹤調試,可供參考。

相關文章
相關標籤/搜索