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

 

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

1、理論知識git

Linux系統的通常執行過程github

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

1. 正在運行的用戶態進程Xshell

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作了關鍵的進程上下文切換.net

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

6. restore_all //恢復現場調試

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

8. 繼續運行用戶態進程Y

幾種特殊狀況

  • 經過中斷處理過程當中的調度時機,用戶態進程與內核線程之間互相切換和內核線程之間互相切換,與最通常的狀況很是相似,只是內核線程運行過程當中發生中斷沒有進程用戶態和內核態的轉換;
  • 內核線程主動調用schedule(),只有進程上下文的切換,沒有發生中斷上下文的切換,與最通常的狀況略簡略;
  • 建立子進程的系統調用在子進程中的執行起點及返回用戶態,如fork;
  • 加載一個新的可執行程序後返回到用戶態的狀況,如execve;

進程的調度時機與進程的切換

操做系統原理中介紹了大量進程調度算法,這些算法從實現的角度看僅僅是從運行隊列中選擇一個新進程,選擇的過程當中運用了不一樣的策略而已。

對於理解操做系統的工做機制,反而是進程的調度時機與進程的切換機制更爲關鍵。

進程調度的時機

 

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

 

進程的切換

 

  • 爲了控制進程的執行,內核必須有能力掛起正在CPU上執行的進程,並恢復之前掛起的某個進程的執行,這叫作進程切換、任務切換、上下文切換;即進程上下文切換!
  • 掛起正在CPU上執行的進程,與中斷時保存現場是不一樣的,中斷先後是在同一個進程上下文中,只是由用戶態轉向內核態執行;
  • 進程上下文包含了進程執行須要的全部信息
    • 用戶地址空間:包括程序代碼,數據,用戶堆棧等
    • 控制信息:進程描述符,內核堆棧等
    • 硬件上下文(注意中斷也要保存硬件上下文只是保存的方法不一樣)
  • schedule()函數選擇一個新的進程來運行,並調用context_switch進行上下文的切換,這個宏調用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 */    \

43           "pushl %%ebp\n\t"        /* save    EBP   */  \

44           "movl %%esp,%[prev_sp]\n\t"  /* save    ESP   */  \

45           "movl %[next_sp],%%esp\n\t"  /* restore ESP   */  \

46           "movl $1f,%[prev_ip]\n\t"    /* save    EIP   */  \

47           "pushl %[next_ip]\n\t"   /* restore EIP   */     \

48           __switch_canary                    \

49           "jmp __switch_to\n"  /* regparm call  */  \

50           "1:\t" /*下一個進程開始執行的地方!*/                        \

51           "popl %%ebp\n\t"     /* restore EBP   */     \

52           "popfl\n"         /* restore flags */   \

53                                   \

54           /* output parameters */                 \

55           : [prev_sp] "=m" (prev->thread.sp),      \

56             [prev_ip] "=m" (prev->thread.ip),         \

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)

 

實驗:

登錄實驗樓虛擬機http://www.shiyanlou.com/courses/195

打開shell終端,執行如下命令:

cd LinuxKernel

rm -rf menu

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

cd menu

mv test_exec.c test.c

make rootfs

能夠看到編譯出來的系統已經有了exec命令


1

能夠經過增長-s -S啓動參數打開調試模式

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

打開gdb進行遠程調試

gdb

file ../linux-3.18.6/vmlinux

target remote:1234

設置斷點

b schedule

b context_switch

b switch_to

b pick_next_task

相關文章
相關標籤/搜索