第八講 進程的切換和系統的一般執行過程
@2015.04
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
操作系統原理中介紹了大量進程調度算法,這些算法從實現的角度看僅僅是從運行隊列中選擇一個新進程,選擇的過程中運用了不同的策略而已。
對於理解操作系統的工作機制,反而是進程的調度時機與進程的切換機制更為關鍵。
#define switch_to(prev, next, last) \
{ \
/* \
* Context-switching clobbers all registers, so we clobber \
* them explicitly, via unused output variables. \
* (EAX and EBP is not listed because EBP is saved/restored \
* explicitly for wchan access and EAX is the return value of \
* __switch_to()) \
*/ \
unsigned long ebx, ecx, edx, esi, edi; \
asm volatile("pushfl\n\t" /* save flags */ \
"pushl %%ebp\n\t" /* save EBP */ \
"movl %%esp,%[prev_sp]\n\t" /* save ESP */ \
"movl %[next_sp],%%esp\n\t" /* restore ESP */ \
"movl $1f,%[prev_ip]\n\t" /* save EIP */ \
"pushl %[next_ip]\n\t" /* restore EIP */ \
__switch_canary \
"jmp __switch_to\n" /* regparm call */ \
"1:\t" /*下一個進程開始執行的地方!*/ \
"popl %%ebp\n\t" /* restore EBP */ \
"popfl\n" /* restore flags */ \
/* output parameters */ \
: [prev_sp] "=m"(prev->thread.sp), \
[prev_ip] "=m"(prev->thread.ip), \
"=a"(last), \
/* clobbered output registers: */ \
"=b"(ebx), "=c"(ecx), "=d"(edx), \
"=S"(esi), "=D"(edi) \
__switch_canary_oparam \
/* input parameters: */ \
: [next_sp] "m"(next->thread.sp), \
[next_ip] "m"(next->thread.ip), \
/* regparm parameters for __switch_to(): */ \
[prev] "a"(prev), \
[next] "d"(next) \
__switch_canary_iparam \
: /* reloaded segment registers */ \
"memory");
} while (0)
###實驗內容
1.理解Linux系統中進程調度的時機,可以在內核代碼中搜索schedule()函數,看都是哪裡調用了schedule(),判斷我們課程內容中的總結是否準確;
2.使用gdb跟蹤分析一個schedule()函數 ,驗證您對Linux系統進程調度與進程切換過程的理解;推薦在實驗樓Linux虛擬機環境下完成實驗。
3.特別關注並仔細分析switch_to中的彙編代碼,理解進程上下文的切換機制,以及與中斷上下文切換的關係;
cd LinuxKernel
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_exec.c test.c
make rootfs
效果如下:
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
並在系統調用
關閉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等函數的執行過程,有圖為證: