進程的切換和系統的通常執行過程

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

一.關於進程調度的基本知識git

      進程的幾種不一樣分類:第一種分類:類型一:I/O-bound:頻繁的進行I/O,一般會花費不少的時間等待I/O操做的完成;類型二:CPU-bound:計算密集型 ,須要大量的CPU時間進行運算第二種分類:類型一:批處理進程 ;類型二:實時進程;類型三:交互式進程。github

      調度策略:是一組規則,它們決定何時以怎樣的方式選擇一個新的進程運行,Linux的調度基於分時和優先級:隨着版本的變化,分時技術在不斷變化,Linux既支持普通的分時進程,也支持實時進程,Linux中的調度是多種調度策略和調度算法的混合。算法

     Linux的進程根據優先級排隊:根據特定的算法計算出進程的優先級,用一個值表示,這個值表示把進程如何適當的分配給CPU。
     Linux中進程的優先級是動態的:調度優先級會根據進程的行爲週期性的調整進程的優先級:較長時間未分配到CPU的進程,一般上升;已經在CPU上運行了較長時間的進程,一般降低。掛起正在CPU上執行的進程,與中斷時保存現場是不一樣的,中斷先後是在同一個進程上下文中,只是由用戶態轉向內核態執行
     進程上下文包含了進程執行所須要的全部信息。
          用戶地址空間:包括程序代碼,數據,用戶堆棧等
          控制信息:進程描述符,內核堆棧等
          硬件上下文(注意中斷也要保存硬件上下文只是保存的方法不一樣)
    Linux系統的通常執行過程分析:這時候咱們能夠有了一個條件來理解linux系統的通常運行狀態,其中有一個用戶態進程X須要切換到用戶進程Y。從正在運行的用戶態進程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:eip/eflags from kernel stack
          8.繼續運行用戶態進程Y
 
二.進程調度關鍵代碼分析
      咱們還有不少的系統調用能夠實現進程的優先級,好比說如下的代碼:
    nice 
    getpriority
    sched_getscheduler/sched_setscheduler
    sched_getparam/sched_setparam
    sched_yield
    sched_get_priority_min/sched_get_priority_max
sched_rr_get_interval
      調度算法策略只是一個策略算法而已,無論使用什麼策略,都是從隊伍中選擇了下一個進程來執行,將調度算法與其餘部分耦合了。
      進程調度的時機須知道如下四點:中斷處理過程(包括時鐘中斷、I\O中斷、系統調用和異常)中,直接調用schedule(),或者返回用戶態的時根據need_resched標記調用schedule();內核線程能夠直接調用schedule()進行進程切換,也能夠在中斷處理過程進行調度,也就是說內核線程做爲一類特殊的進程能夠主動,也能夠被動調度;用戶態進程沒法實現主動調度,僅能經過陷入內核態的某個時機進行調度,即在中斷處理過程當中進行調度;用戶態只能被動調度,內核線程是隻有內核態沒有用戶態的特殊進程,內核線程能夠主動調度也能夠被動調度。
      schedule函數:schedule函數實現調度,目的:在運行隊伍中找到一個進程,把CPU分配給它,調用方法:直接使用schedule();鬆散調用,根據need_resched標記
next = pick_next_task(rq, prev); //進程調度算法都封裝這個函數內部
context_switch(rq, prev, next); //進程上下文切換
switch_to切換堆棧和寄存器的狀態,利用了prev和next兩個參數:prev指向當前進程,next指向被調度的進程
  進程上下文調度相關代碼分析:在switch()中:
31#define switch_to(prev, next, last)                    
32do {                                 
33  /*                              
34   * Context-switching clobbers all registers, so we clobber  
35   * them explicitly, via unused output variables.     
36   * (EAX and EBP is not listed because EBP is saved/restored  
37   * explicitly for wchan access and EAX is the return value of   
38   * __switch_to())                     
39   */                                
40  unsigned long ebx, ecx, edx, esi, edi;                
41                                  
42  asm volatile("pushfl\n\t"      /* save    flags */          //保存當前進程的flags
43           "pushl %%ebp\n\t"        /* save    EBP   */       //把當前進程的堆棧基址壓棧
44           "movl %%esp,%[prev_sp]\n\t"  /* save    ESP   */   //把當前的棧頂保存到prev->thread.sp
45           "movl %[next_sp],%%esp\n\t"  /* restore ESP   */   //把下一個進程的棧頂保存到esp中,這兩句完成了內核堆棧的切換
46           "movl $1f,%[prev_ip]\n\t"    /* save    EIP   */   //保存當前進程的EIP,能夠從這恢復
47           "pushl %[next_ip]\n\t"   /* restore EIP   */       //把下一個進程的起點位置壓到堆棧,就是next進程的棧頂。next_ip通常是$1f,對於新建立的子進程是ret_from_fork
             //通常用return直接把next_ip pop出來
48           __switch_canary                   
49           "jmp __switch_to\n"  /* regparm call  */  //jmp經過寄存器傳遞參數,即後面的a,d。 函數__switch_to也有return把next_ip pop出來     
50           "1:\t"              //認爲從這開始執行next進程(EIP角度),第一條指令是next_ip這個起點,但前面已經完成內核堆棧的切換,早就是next進程的內核堆棧(算prev進程,比較模糊)          
51           "popl %%ebp\n\t"     /* restore EBP   */  //next進程曾經是prev進程,壓棧過ebp  
52           "popfl\n"         /* restore flags */  
53                                  
54           /* output parameters */                
55           : [prev_sp] "=m" (prev->thread.sp),     //當前進程的,在中斷內部,在內核態,sp是內核堆棧的棧頂
56             [prev_ip] "=m" (prev->thread.ip),     //當前進程的EIP  
57             "=a" (last),                 
58                                  
59             /* clobbered output registers: */     
60             "=b" (ebx), "=c" (ecx), "=d" (edx),      
61             "=S" (esi), "=D" (edi)             
62                                       
63             __switch_canary_oparam                
64                                  
65             /* input parameters: */                
66           : [next_sp]  "m" (next->thread.sp),    //下一個進程的內核堆棧的棧頂     
67             [next_ip]  "m" (next->thread.ip),    //下一個進程的執行起點
68                                       
69             /* regparm parameters for __switch_to(): */  
70             [prev]     "a" (prev),               //寄存器的傳遞 
71             [next]     "d" (next)               
72                                  
73             __switch_canary_iparam                
74                                  
75           : /* reloaded segment registers */           
76          "memory");                  
77} while (0)
三.Linux系統執行過程當中的幾個特殊狀況:
1.經過中斷處理過程當中的調度時機,用戶態進程與內核線程之間互相切換和內核線程之間互相切換,與最通常的狀況很是相似,只是內核線程運行過程當中發生中斷沒有進程用戶態和內核態的轉換;
2.內核線程主動調用schdule(),只有進程上下文的切換,沒有中斷上下文的切換,與最通常的狀況略簡略;
3.建立子進程的系統調用在子進程中的執行起點及返回用戶態,如fork;
4.加載一個新的可執行程序後返回到用戶態的狀況,如execve;
四.視頻中的小總結:
 Linux操做系統架構概覽咱們在這裏總結一下這個課學習的內容:
任何計算機系統都包含一個基本的程序集合,稱爲操做系統
-內核(進程管理,進程調度,進程間通信機制,內存管理,中斷異常處理,文件系統,I/O系統,網絡部分)
-其它程序(例如函數庫,shell程序、系統程序等等)
操做系統的目的
-與硬件交互,管理全部的硬件資源
-爲用戶程序(應用程序)提供一個良好的執行環境
那麼咱們能夠看一張圖:最底層有磁盤管理,物理內存的管理,內存控制器還有控制檯,這個地方它有一個Kernel Intelface to the hardware,
也就是對硬件資源的管理,而後呢在上一層就是內核的實現,內核的實現也就是CPU調度,內存管理,按需調度,虛擬內存等等,還有其餘的驅動,磁盤管理,文件系統等等,這些操做系統的內核,咱們這門課分析的內核只是計算機操做系統裏面最最關鍵的一些代碼,實際上整個操做系統很是複雜,不少其餘的部分都沒有涉及。那麼呢,這個地方有一個系統調用接口,這裏涉及到不一樣的部分,基礎軟件也就是share共享庫lib ,動態加載器這些等等。

2.ls命令——最簡單與最複雜的操做shell

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

1.從在CPU執行指令的角度看:網絡

2.從內存的角度看架構

五.實驗過程截圖及分析函數

搭建實驗環境學習

cd LinuxKernel   
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_exec.c test.c
make rootfs

gdb調試spa

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
gdb
file ../linux-3.18.6/vmlinux
target remote:1234
設置斷點:
b schedule
b pick_next_task
b context_switch
b switch_to

3.在schedule處設置斷點,運行,並用list展開函數

4.單步運行,直至__schedule()

5.在context_switch處設立斷點,執行

六.知識總結

    進程調度程序是內核重要的組成部分,由於運行着的進程首先在使用計算機(至少在咱們大多數人看來)。然而,知足進程調度的各類須要毫不是垂手可得的,很難找到「一刀切」的算棒,既適合衆多的可運行進程,又具備可伸縮性,還能在調度週期和吞吐量之間求得平衡,同時還知足各類負載的需求。不過, Linux 內核的新CFS 調度程序儘可能知足了各個方面的需求,並以較完善的可伸縮性和新穎的方挫提供了最佳的解決方案。前面的章節覆蓋了進程管理的相關內容,本章則考察了進程調度所遵循的基本原理、具體實現、調度算能以及目前Linux 內核所使用的接口。

相關文章
相關標籤/搜索