以fork()函數為例,分析內核態進程切換的實現
首先在用戶態的某個進程中執行了fork()函數
fork引發中斷,切入內核,內核棧綁定用戶棧
首先分析五段論中的第一段:
中斷入口:先把相關寄存器壓棧保存,然后call真正的fork系統調用
當前進程被阻塞或時間片到后,使用調度算法進行線程切換
reschedule的展開:其實是把ret_from_sys_call的地址壓棧(作用之后就會看到),然后再去進行調度算法
此時的內核棧:??2后是內核當前的esp指針
第五段和調度算法:執行完調度算法后,即cpu已經調度到新的進程,此時從內核態返回用戶態,這時就要用到ret_from_sys_cal
注意eax里存的是返回值,返回的位置是新的進程
中間三段論:
switch_to:把cpu從一個內核棧調度到另一個內核棧,即找到目標進程的tcb,這樣就完成了一次切換。
但是linux0.11用了tss方式,即用tcb保留當前進程的運行情況(保留下所有相關寄存器的值,可以理解為運行現場的照相),然后把新的進程的tcb保存的運行現場扣給所有寄存器
內嵌匯編注釋:
:新TSS描述符賦值給TR(32位)
:把TR賦值給tmp.b
:跳轉到tmp.a執行,ljmp是長跳轉指令,需要64位的目標操作數
因為fork的工作時建立一個新的進程
copy_process函數里需要把父進程的所有寄存器信息賦值給子進程(子進程目前和父進程是一樣的)
創建子進程的內核棧和綁定的用戶棧
給p分配一頁內核空間,esp0指向棧頂,由於子進程和父進程共用用戶棧,所以綁定的用戶棧和父進程也一樣
copy時的一些細節:
因為是fork所以新的tss先復制舊的tss
然后進程切換,內存也跟着切換
eax要變成0(之后會說)
因為新的進程需要被調度,所以狀態設置為0
同時也必須填寫兩個棧
fork的一些特別之處
1.返回值有兩個:父進程非0,子進程為0,(res和eax是綁定的,子進程的eax在tss中被置為0了,而父進程的eax在第一段里被壓棧保存了,返回值是子進程pid)
2.子進程在被創建后,返回到用戶態運行時會阻塞父進程,那么父進程什么時候返回?是子進程阻塞或退出后cpu調度到父進程時,通過iret返回到父進程對應的用戶態的
那么調用fork的結果就是:父進程等待,子進程運行
exec系統調用可以調用cmd命令:exec返回前,子進程執行和父進程一樣的代碼,返回后就,子進程就開始調用cmd,和父進程不一樣了
那么如何才能讓子進程找到a並開始運行?
調用do_execve后,a在編譯鏈接時會產生一個鏈接地址(入口地址entry),通常a的第一句就是a的入口地址,從這個入口地址進入就可以按指令運行a了
所以exec就是找到a的入口地址,將其賦值給ret作為用戶棧的返回地址返回,那么返回后就可以直接從a入口地址開始運行a了