內核級線程實現(哈工大李志軍)

進入中斷

在這裏插入圖片描述
main()中:

  • 首先在A()函數中系統調用fork(),將B()的地址壓入用戶棧
  • fork() 引起中斷0x80,進入內核。
  • 執行int 0x80時,還未進入內核,首先找到內核棧,壓入當前棧地址(即用戶棧);壓入當前CS:IP(用戶態)(ret = CS:IP)
  • 進入內核,執行system_call

進入內核

在這裏插入圖片描述
在這裏插入圖片描述

  • 剛進入內核,首先在內核態中的各種寄存器壓到棧中,即保護現場。

  • 執行sys_fork(),繼續向下執行

  • state(%eax)相當於state + _current,與 0(就緒或運行態)作比較,非0即阻塞,_currentPCB,阻塞則調度(reschedule)。

  • 接下來再次判斷counter + _current 判斷是否時間片用盡,若是則切換(reschedule)

  • 執行中斷返回(ret_from_sys_call)

  • reschedule:
    在這裏插入圖片描述
    執行的是_schedule().

schedule

在這裏插入圖片描述

  • next是下一個進程的PCB
  • 核心是switch_to

在這裏插入圖片描述

  • linux 0.11 中基於TSS(Task Struct Segement) 切換,實驗中將其改爲基於內核棧的切換,TSS切換速度較慢,現代操作系統中大多爲內核棧切換。
  • TSS 類似於內存快照,ljmp改變了當前TSS,所以CPU需要從新的TSS重新加載所有寄存器。

中斷出口

在這裏插入圖片描述
還原現場,並恢復到用戶態。

sys_fork()

創建一個進程(或內核級線程),就是要做成能切換的樣子。
在這裏插入圖片描述

  • copy_process 將父進程的棧都作爲參數,C語言中參數越靠後越靠近棧頂。

創建棧:
在這裏插入圖片描述
- get_free_page() 申請內存空間,注意:malloc()是用戶態代碼。
- 設置TSS,使其能夠切換。
- 父子線程用戶棧相同,內核棧不同。
在這裏插入圖片描述

  • 子進程的eax = 0,由於父進程和子進程的關係,它們都將執行int 0x80的下一個代碼:mov res,%eax
    在這裏插入圖片描述
    而子進程將返回0,於是觀察fork()經典的使用:在這裏插入圖片描述
    子進程將進入if塊內,調用exec,子進程將被更換新的代碼:
    在這裏插入圖片描述
    更換新的代碼,我們知道iret指令將把棧彈出,這時CS:EIP將被更改到用戶態代碼段,那麼我們只需要更改棧中儲存的CS:IP即可,先偏移量EIP=0x1C,並將EIP+%esp壓入棧中,即EIP在棧中位置,執行do_execve
    do_execve,將程序入口地址給eip,更改代碼段;eip[3]正好等於SP,更改棧。
    在這裏插入圖片描述

總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章