Linux0.11內核源碼——內核態線程(進程)切換的實現


以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了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM