[039][x86匯編語言]第十五章 任務切換 內存示意圖 以及 TSS相關 源碼解析

學習筆記

《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,登記的內容就是任務BTSS基地址、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格式 http://www.lxweimin.com/p/adb70daa6d2c

        ;訪問用戶程序頭部,獲取數據填充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中已經填寫完畢
                        
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容