《Linux內核分析》第八週筆記 進程的切換和系統的通常執行過程

20135132陳雨鑫 + 原創做品轉載請註明出處 + 《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000 」linux

1、進程調度與進程調度的時機分析算法

  一、進程調度shell

  不一樣類型的進程有不一樣的調度需求架構

  第一種分類
      I/O-bound
           頻繁的進行I/O
           一般會花費不少時間等待I/O操做的完成
     CPU-bound
           計算密集型
           須要大量的CPU時間進行運算
  第二種分類
     批處理進程(batch process)
           沒必要與用戶交互,一般在後臺運行
           沒必要很快響應
           典型的批處理程序:編譯程序、科學計算
     實時進程(real-time process)
           有實時需求,不該被低優先級的進程阻塞
           響應時間要短、要穩定
           典型的實時進程:視頻/音頻、機械控制等
       交互式進程(interactive process)
           須要常常與用戶交互,所以要花不少時間等待用戶輸入操做
           響應時間要快,平均延遲要低於50~150ms
          典型的交互式程序:shell、文本編輯程序、圖形應用程序等
框架

  內核中的調度算法相關代碼使用了相似OOD的策略模式。函數

  二、進程調度的時機學習

  • 中斷處理過程(包括時鐘中斷、I/O中斷、系統調用和異常)中,直接調用schedule(),或者返回用戶態時根據need_resched標記調用schedule();spa

   

  • 內核線程能夠直接調用schedule()進行進程切換,也能夠在中斷處理過程當中進行調度,也就是說內核線程做爲一類的特殊的進程能夠主動調度,也能夠被動調度;操作系統

  • 用戶態進程沒法實現主動調度,僅能經過陷入內核態後的某個時機點進行調度,即在中斷處理過程當中進行調度。線程

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

  一、進程的切換

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

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

    3)進程上下文包含了進程執行須要的全部信息

    • 用戶地址空間: 包括程序代碼,數據,用戶堆棧等

    • 控制信息 :進程描述符,內核堆棧等

    • 硬件上下文(注意中斷也要保存硬件上下文只是保存的方法不一樣)

    4)schedule()函數選擇一個新的進程來運行,並調用context_switch進行上下文的切換,這個宏調用switch_to來進行關鍵上下文切換

    •next = pick_next_task(rq, prev);//進程調度算法都封裝這個函數內部

    •context_switch(rq, prev, next);//進程上下文切換

    •switch_to利用了prev和next兩個參數:prev指向當前進程,next指向被調度的進程

  二、代碼分析

  關鍵彙編代碼:

 

    outout: thread.sp:內核態,sp是內核堆棧的棧頂

        thread.ip:當前進程的eip

    input: prev_sp:下一個進程的內核堆棧的棧頂

        prev_ip:下一個進程執行的起點

 

這兩句完成了內核堆棧的切換,將當前內核堆棧的棧頂保存起來,把下一個next進程的棧頂放到ESP寄存器中,以後的壓棧動做都是在next進程堆棧中完成:

next_ip通常是$1f,對於新建立的子進程是ret_from_fork。

3、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;

    next_ip=ret_from_fork

  三、進程的地址空間一共有4G,其中0——3G是用戶態能夠訪問,3G以上只有內核態能夠訪問

 4、系統架構和執行過程概述

  一、系統架構

  二、典型的Linux操做系統的架構

  三、最簡單也是最複雜的操做——ls

  四、CPU和內存的角度看Linux系統的執行

  1. 執行gets()函數;
  2. 系統調用,陷入內核態,將eip/esp/cs/ds等信息壓棧。
  3. 進程管理:等待鍵盤敲入指令。等待輸入,CPU會調度其餘進程執行,同時wait一個I/O中斷;
  4. 敲擊ls,發I/O中斷給CPU,中斷處理程序進行現場保存、壓棧等等;
  5. 中斷處理程序發現X進程在等待這個I/O(此時X已經變成阻塞態),處理程序將X設置爲WAKE_UP;
  6. 進程管理可能會把進程X設置爲next進程;
  7. gets()的系統調用就得到了從鍵盤上讀取的數據,返回用戶態。

從內存角度看,全部的物理地址都會被映射到3G以上的地址空間:由於這部分對全部進程來講都是共享的

0xc0000000如下是3G的部分,用戶態。

 

 

5、實驗

使用gdb跟蹤分析一個schedule()函數 ,驗證對Linux系統進程調度與進程切換過程的理解

 

 

關閉QEMU窗口,在shell窗口中,cd LinuxKernel回退到LinuxKernel目錄,使用下面的命令啓動內核並在CPU運行代碼前停下以便調試:

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

接下來,咱們就能夠水平分割一個新的shell窗口出來,依次使用下面的命令啓動gdb調試

gdb

(gdb) file linux-3.18.6/vmlinux

(gdb) target remote:1234

並在內核函數schedule的入口處設置斷點,接下來輸入c繼續執行,則系統便可停在該函數處,接下來咱們就可使用命令n或者s逐步跟蹤,能夠詳細瀏覽pick_next_task,switch_to等函數的執行過程

設置斷點在schedule處:

6、總結

  經過學習,咱們瞭解到Linux使用了堆棧進行了進程調度。schedule()在須要的時候從新得到大內核鎖、從新啓用內核搶佔、並檢查是否一些其餘的進程已經設置了當前進程的tlf_need_resched標誌,若是是,整個schedule()函數從新開始執行,不然,函數結束。linux調度的核心函數爲schedule,schedule函數封裝了內核調度的框架。細節實現上調用具體的調度類中的函數實現。當切換進程已經選好後,就開始用戶虛擬空間的處理,而後就是進程的切換switch_to()。所謂進程的切換主要就是堆棧的切換,這是由宏操做switch_to()完成的。

相關文章
相關標籤/搜索