歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

Linux內核分析——進程的切換和系統的一般執行過程

            進程的切換和系統的一般執行過程

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

(一)進程調度與進程調度的時機分析

  1、不同類型的進程有不同的調度需求

  第一種分類:

    (1)I/O-bound:頻繁進行I/O,花費很多時間等待I/O操作的完成。

    (2)CPU-bound:計算密集型,需要大量CPU時間進行計算。

  第二種分類:

    (1)批處理進程:不必交互、很快響應。

    (2)實時進程:要求響應時間短。

    (3)交互式進程(shell)。

  2、調度策略:是一組規則,它們決定什麼時候以怎樣的方式選擇一個新進程運行。

  3、linux進程調度是基於分時和優先級的。

  4、Linux的進程根據優先級排隊。

  5、Linux中進程的優先級是動態的。

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

  7、進程調度的時機:

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

    (2)內核線程(只有內核態沒有用戶態的特殊進程)可以直接調用schedule()進行進程切換,也可以在中斷處理過程中進行調度,也就是說內核線程作為一類的特殊的進程可以主動調度,也可以被動調度;

    (3)用戶態進程無法實現主動調度,只能被動調度,僅能通過陷入內核態後的某個時機點進行調度,即在中斷處理過程中進行調度。

(二)進程上下文切換相關代碼分析

  1、進程的切換

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

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

    (3)進程上下文包含了進程執行需要的所有信息

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

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

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

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

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

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

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

  3、schedule()中:

   

  

  4、context_switch中:

   

  5、switch_to中:

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系統的一般執行過程

(一)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

(二)Linux系統執行過程中的幾個特殊情況

  1、幾種特殊情況

    (1)通過中斷處理過程中的調度時機,用戶態進程與內核線程之間互相切換和內核線程之間互相切換,與最一般的情況非常類似,只是內核線程運行過程中發生中斷沒有進程用戶態和內核態的轉換;

    (2)內核線程主動調用schedule(),只有進程上下文的切換,沒有發生中斷上下文的切換,與最一般的情況略簡略;

    (3)創建子進程的系統調用在子進程中的執行起點及返回用戶態,如fork;

    (4)加載一個新的可執行程序後返回到用戶態的情況,如execve;

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

(一)Linux操作系統架構概覽

   

  

(二)最簡單也是最復雜的操作--執行ls命令

   

(三)從CPU和內存的角度看Linux系統的執行

  1、CPU執行指令的角度:

   

  2、內存的角度:

   

四、實驗

  1、環境搭建

    cd LinuxKernel  

    rm menu -rf

    git clone https://github.com/mengning/menu.git

    cd menu

    mv test_exec.c test.c

    make rootfs

   

  

  2、gdb調試

    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

  3、設置斷點

    b schedule

    b pick_next_task

    b context_switch

    b switch_to

   

   

   

   

五、總結

  1、Linux進程調度是基於分時和優先級的。

  2、Linux中,內核線程是只有內核態沒有用戶態的特殊進程。

  3、內核可以看作各種中斷處理過程和內核線程的集合。

  4、Linux系統的一般執行過程 可以抽象成正在運行的用戶態進程X切換到運行用戶態進程Y的過程。

  5、Linux中,內核線程可以主動調度,主動調度時不需要中斷上下文的切換。

  6、Linux內核調用schedule()函數進行調度,並調用context_switch進行上下文的切換,這個宏調用switch_to來進行關鍵上下文切換。

Copyright © Linux教程網 All Rights Reserved