學習筆記
《x86匯編語言:從實模式到保護模式》
http://www.lxweimin.com/p/d481cb547e9f
運行結果
第十五章 任務切換 運行結果.png
代碼使用
-
nasmide.exe 編譯源碼文件 :加載程序
c13_mbr.asm
,內核程序c15_core.asm
,用戶程序c15.asm
; -
fixvhdwr.exe 寫扇區二進制
.bin
文件:c13_mbr.bin(邏輯扇區號LBA:0)
、c15_core.bin(LBA:1)
、c15.bin(LBA:50)
; - 運行
VM virtual box
.
執行完兩次任務切換(CALL一次,JMP一次)后的內存以及GDT狀態
第15章 執行完兩次任務切換后的內存示意圖.png
第15章 執行完兩次任務切換后的GDT示意圖.png
- 如圖所示,被加載到內存的,紅色文字以及綠色文字代表的內容均來自于同一個用戶程序
c15.asm
; - 這說明,一個程序可以對應著多個運行中的副本,或者說多個任務,它們彼此之間沒有任何關系,在內存中的位置不同,運行狀態也不一樣;
- 正因如此,即便兩次切換都是切換到同一個用戶程序,但本質上是切換到不同的任務,加上沒有寫任何內存管理的代碼(比如重新釋放內存),之前的任務一直留在內存里;
任務管理器
-
任務管理器,又稱程序管理器:創建
0特權級
的內核任務,并將當前正在執行的內核代碼段劃歸該任務,當前代碼的作用是創建其他任務,管理它們,所以稱做任務管理器或者叫程序管理器;
三個TSS描述符
[如圖黑色和灰色部分文字]程序管理器的TSS描述符(位于內核程序c15_core.asm
:標號start
后面)
;為程序管理器的TSS分配內存空間
mov ecx,104 ;為該任務的TSS分配內存
call sys_routine_seg_sel:allocate_memory
mov [prgman_tss+0x00],ecx ;保存程序管理器的TSS基地址
;在程序管理器的TSS中設置必要的項目
mov word [es:ecx+96],0 ;沒有LDT。處理器允許沒有LDT的任務。
mov word [es:ecx+102],103 ;沒有I/O位圖。0特權級事實上不需要。
mov word [es:ecx+0],0 ;反向鏈=0
mov dword [es:ecx+28],0 ;登記CR3(PDBR)
mov word [es:ecx+100],0 ;T=0
;不需要0、1、2特權級堆棧。0特級不
;會向低特權級轉移控制。
;創建TSS描述符,并安裝到GDT中
mov eax,ecx ;TSS的起始線性地址
mov ebx,103 ;段長度(界限)
mov ecx,0x00408900 ;TSS描述符,特權級0
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
mov [prgman_tss+0x04],cx ;保存程序管理器的TSS描述符選擇子
;任務寄存器TR中的內容是任務存在的標志,該內容也決定了當前任務是誰。
;下面的指令為當前正在執行的0特權級任務“程序管理器”后補手續(TSS)。
ltr cx
- 程序管理器可以沒有自己的LDT,可以將自己使用的段描述符安裝在GDT中;
-
mov word [es:ecx+0],0
,設置TSS指針域(結合TSS的格式來看,此時ES指向0~4GB內存空間,ECX是TSS基地址
) - 程序管理器的TSS描述符必須安裝在GDT中;
- 使用指令
ltr
,將當前任務的TSS選擇子傳送到處理器的TR寄存器;
ltr指令執行后,處理器用該選擇子訪問GDT
找到相對應的TSS描述符,將其B位置“1”
表示該任務正在執行中(或者處于掛起狀態)
同時,還要將該描述符傳送到TR寄存器的描述符高速緩存器中。
子程序load_relocate_program 里TSS相關代碼(位于內核程序c15_core.asm
)
TSS 與TCB 一 一對應
- 登記 TSS基地址、TSS選擇子 到 TCB(這樣理解,從任務A切換到任務B時,這里登記用的就是任務B的TCB,登記的內容就是任務B的 TSS基地址、TSS選擇子,保證一個任務、一個TCB、一個TSS);
- 第15章的代碼,是從內核程序切換到用戶程序的,那么內核程序就是任務A,用戶程序就是任務B;
- 并且,內核程序是不需要TCB的;
- 如圖所示,第15章的代碼要分別用call 以及 jmp 各完成一次切換,同一次任務我都用相同的顏色畫了TCB和TSS;
;創建用戶程序的TSS
mov ecx,104 ;tss的基本尺寸
mov [es:esi+0x12],cx
dec word [es:esi+0x12] ;登記TSS界限值到TCB
call sys_routine_seg_sel:allocate_memory
mov [es:esi+0x14],ecx ;登記TSS基地址到TCB
...
;在GDT中登記TSS描述符
mov eax,[es:esi+0x14] ;TSS的起始線性地址
movzx ebx,word [es:esi+0x12] ;段長度(界限)
mov ecx,0x00408900 ;TSS描述符,特權級0
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
mov [es:esi+0x18],cx ;登記TSS選擇子到TCB
完整填寫TSS
- 注意這里填的就是TSS的內容,不是TSS描述符;
- 根據注釋以及TSS格式來一一填寫即可
;訪問用戶程序頭部,獲取數據填充TSS
mov ebx,[ebp+11*4] ;從堆棧中取得TCB的基地址
mov edi,[es:ebx+0x06] ;取得用戶程序加載基地址
mov edx,[es:edi+0x10] ;登記程序入口點 EIP
mov [es:ecx+32],edx ;到TSS
mov dx,[es:edi+0x14] ;登記程序代碼段 CS 選擇子
mov [es:ecx+76],dx ;到TSS
mov dx,[es:edi+0x08] ;登記堆棧段 SS 選擇子
mov [es:ecx+80],dx
mov dx,[es:edi+0x04] ;登記 程序數據段 DS(指向程序頭部段)
mov word [es:ecx+84],dx
mov word [es:ecx+72],0 ;TSS中的 ES=0
mov word [es:ecx+88],0 ;TSS中的 FS=0
mov word [es:ecx+92],0 ;TSS中的 GS=0
pushfd ;將EFLAGS寄存器的內容壓入棧
pop edx ;再將其彈出到EDX寄存器
mov dword [es:ecx+36],edx ;登記TSS中的EFLAGS
[如圖紅色文字部分]使用call指令進行任務切換(位于內核程序c15_core.asm
:標號start
后面)
;創建TCB 這個TCB屬于即將切換去的任務
mov ecx,0x46
call sys_routine_seg_sel:allocate_memory
call append_to_tcb_link ;將此TCB添加到TCB鏈中
push dword 50 ;用戶程序位于邏輯50扇區
push ecx ;壓入任務控制塊起始線性地址
call load_relocate_program ; 會往創建屬于要切換去的任務的TSS★
; 會往TCB里面填寫TSS選擇子★
;32位間接遠調用指令CALL
call far [es:ecx+0x14] ;執行任務切換
;ECX指向要切換任務(用戶程序)的TCB
;從TCB中取出TSS基地址、TSS選擇子
;CPU發現這是TSS選擇子,就知道要執行任務切換
;新任務的TSS完整內容在子程序load_relocate_program中已經填寫完畢
[如圖綠色文字部分]使用jmp指令進行任務切換(位于內核程序c15_core.asm
:標號start
后面)
;創建TCB 這個TCB屬于即將切換去的任務
mov ecx,0x46
call sys_routine_seg_sel:allocate_memory
call append_to_tcb_link ;將此TCB添加到TCB鏈中
push dword 50 ;用戶程序位于邏輯50扇區
push ecx ;壓入任務控制塊起始線性地址
call load_relocate_program ; 會往創建屬于要切換去的任務的TSS★
; 會往TCB里面填寫TSS選擇子★
;32位間接遠轉移指令JMP
jmp far [es:ecx+0x14] ;執行任務切換
;ECX指向要切換任務(用戶程序)的TCB
;從TCB中取出TSS基地址、TSS選擇子
;CPU發現這是TSS選擇子,就知道要執行任務切換
;新任務的TSS完整內容在子程序load_relocate_program中已經填寫完畢