任务切换——《x86汇编语言:从实模式到保护模式》读书笔记38
任務切換——《x86匯編語言:從實模式到保護模式》讀書筆記38
1. 協同式多任務與搶占式多任務
有兩種基本的任務切換方式,一種是協同式的,一種是搶占式的。
1.1 協同式
從一個任務切換到另一個任務,需要當前任務主動地請求暫時放棄執行權,或者在通過調用門請求操作系統服務時,由操作系統趁機將控制轉移到另一個任務。這種方式依賴于每個任務的“自律”性,當一個任務失控時,其他任務可能得不到執行的機會。
1.2 搶占式
可以安裝一個定時器中斷,并在中斷服務例程中實施任務切換。硬件中斷信號總會定時出現,在這種情況下,每個任務都能獲得平等的執行機會。而且,即使一個任務失控,也不會導致其他任務沒有機會執行。
搶占式多任務將在第17章學習。這一章,我們通過代碼學習任務切換的幾種方法,以及它們各自的特點。
關于任務切換的幾種方法,可以參考我的博文:
任務切換的方法——《x86匯編語言:從實模式到保護模式》讀書筆記36
2. 代碼清單
本章實驗需要3個源文件,分別是:
c13_mbr.asm(依然用第13章的引導程序)
c15_core.asm(第689和706行少了dword,應該加上)
c15.asm
3. 代碼講解
首先有幾點需要說明:
1. 處理器在剛進入保護模式的時候,是以0特權級運行的;
2. 任務不一定非得是3特權級,也可以是0特權級;
3. 操作系統除了為每一個任務提供服務外,也會有一個作為任務而獨立存在的部分,而且是0特權級別的任務,以完成一些管理和控制的功能。
內核開始的工作和上一章相同:主要是顯示處理器的品牌信息,安裝調用門。接下來的工作是創建0特權級的內核任務。
3.1 創建0特權級的內核任務——程序管理器
3.1.1 為程序管理器的TSS分配內存
908 ;為程序管理器的TSS分配內存空間 909 mov ecx,104 ;為該任務的TSS分配內存 910 call sys_routine_seg_sel:allocate_memory 911 mov [prgman_tss+0x00],ecx ;保存程序管理器的TSS基地址注意,為了追蹤程序管理器的TSS,需要保存它的基地址和選擇子。為此,作者在內核數據段聲明并且初始化了6個字節。
430 ;程序管理器的任務信息 431 prgman_tss dd 0 ;程序管理器的TSS基地址 432 dw 0 ;程序管理器的TSS描述符選擇子3.1.2 填寫程序管理器的TSS
913 ;在程序管理器的TSS中設置必要的項目 914 mov word [es:ecx+96],0 ;沒有LDT。處理器允許沒有LDT的任務。 915 mov word [es:ecx+102],103 ;沒有I/O位圖。0特權級事實上不需要。 916 mov word [es:ecx+0],0 ;反向鏈=0 917 mov dword [es:ecx+28],0 ;登記CR3(PDBR) 918 mov word [es:ecx+100],0 ;T=0 919 ;不需要0、1、2特權級堆棧。0特級不 920 ;會向低特權級轉移控制。3.1.3 在GDT中創建TSS描述符
922 ;創建TSS描述符,并安裝到GDT中 923 mov eax,ecx ;TSS的起始線性地址 924 mov ebx,103 ;段長度(界限) 925 mov ecx,0x00408900 ;TSS描述符,特權級0 926 call sys_routine_seg_sel:make_seg_descriptor 927 call sys_routine_seg_sel:set_up_gdt_descriptor 928 mov [prgman_tss+0x04],cx ;保存程序管理器的TSS描述符選擇子注意:TSS描述符只能安裝在GDT中。
3.1.4 加載TR寄存器
930 ;任務寄存器TR中的內容是任務存在的標志,該內容也決定了當前任務是誰。 931 ;下面的指令為當前正在執行的0特權級任務“程序管理器”后補手續(TSS)。 932 ltr cx第932行執行后,處理器用該選擇子(CX的值)訪問GDT,找到對應的TSS描述符,將其B位置1,表示該任務正在執行中(或者處于掛起狀態);同時,還要把該TSS描述符傳送到TR的描述符高速緩存器中。此時,可認為“程序管理器”任務正在執行中。
3.2 加載用戶程序
3.2.1 分配一個任務控制塊(TCB)
938 mov ecx,0x46 939 call sys_routine_seg_sel:allocate_memory 940 call append_to_tcb_link ;將此TCB添加到TCB鏈中任務控制塊依然用第14章的結構。
3.2.2 調用過程load_relocate_program
942 push dword 50 ;用戶程序位于邏輯50扇區 943 push ecx ;壓入任務控制塊起始線性地址 944 945 call load_relocate_program過程load_relocate_program的代碼和上一章相比沒有太大的變化,僅僅是對TSS的填寫比較完整。
3.2.3 通過CALL指令切換到用戶任務
947 call far [es:ecx+0x14] ;執行任務切換。和上一章不同,任務切 948 ;換時要恢復TSS內容,所以在創建任務 949 ;時TSS要填寫完整第947行是一個間接遠調用指令,在TCB偏移為0x14的地方,應該先是一個32位的段內偏移,然后是一個16位的代碼段選擇子或者調用門選擇子。
但是,結合TCB的結構圖,我們發現先是一個TSS基地址(丟棄不用),然后是一個TSS的選擇子。雖然有點奇怪,但是卻合法。當處理器發現是TSS的選擇子,就會執行任務切換。主要過程如下:
1. 保護現場,因為當前正在執行的任務是由TR指示的,所以要把每個寄存器的快照保存到由TR指向的TSS中。這些寄存器包括:GS,FS,DS,SS,CS,ES,EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX,EFLAGS,EIP.
2. 處理器根據TSS選擇子索引GDT,找到新任務的TSS描述符,進而找到新任務的TSS,從中恢復各個寄存器的內容,包括通用寄存器、EFLAGES、段寄存器、EIP、ESP、LDTR等。
3. TR指向新任務的TSS,處理器開始執行新的任務。
下圖對理解這個過程很有幫助。
不過,上面的總結不夠完整,還隱去了一些細節,比如權限的檢查(以后的博文會提到)。
另外,新舊任務的TSS的B位,EFLAGS的NT位,任務鏈接域的變化也是需要關注的。好在作者已經總結成了表格,這里我們再次復習一下。
3.3 用戶程序的執行與切換到程序管理器
用戶代碼比較簡單。請注意最后一行
74 call far [fs:TerminateProgram]這句是通過調用門轉到全局空間執行。
354 terminate_current_task: ;終止當前任務 355 ;注意,執行此例程時,當前任務仍在 356 ;運行中。此例程其實也是當前任務的 357 ;一部分 358 pushfd 359 mov edx,[esp] ;獲得EFLAGS寄存器內容 360 add esp,4 ;恢復堆棧指針 361 362 mov eax,core_data_seg_sel 363 mov ds,eax 364 365 test dx,0100_0000_0000_0000B ;測試NT位 366 jnz .b1 ;當前任務是嵌套的,到.b1執行iretd 367 mov ebx,core_msg1 ;當前任務不是嵌套的,直接切換到 368 call sys_routine_seg_sel:put_string 369 jmp far [prgman_tss] ;程序管理器任務 370 371 .b1: 372 mov ebx,core_msg0 373 call sys_routine_seg_sel:put_string 374 iretd358~360:獲得EFLAGS寄存器的值,值保存在EDX中;
365:測試EFLAGS的NT位。本實驗的場景是,程序管理器可以用JMP指令或者CALL指令切換到用戶任務。如果是前者,那么用戶任務的NT=0,需要用JMP指令切換到程序管理器;如果是后者,那么用戶任務的NT=1,需要用IRET指令返回到程序管理器。
3.4 程序管理器再次加載用戶任務
因為之前程序管理器通過CALL切換到了用戶任務,所以用戶任務會通過IRET返回,所以第374行會執行。接下來又回到了程序管理器,從它中斷的地方繼續執行,也就是第952行。
51 ;重新加載并切換任務 952 mov ebx,prgman_msg2 953 call sys_routine_seg_sel:put_string 954 955 mov ecx,0x46 956 call sys_routine_seg_sel:allocate_memory 957 call append_to_tcb_link ;將此TCB添加到TCB鏈中 958 959 push dword 50 ;用戶程序位于邏輯50扇區 960 push ecx ;壓入任務控制塊起始線性地址 961 962 call load_relocate_program955~962:再次加載用戶任務,依然是第50扇區的程序。這充分說明:一個程序可以對應著多個運行中的副本,或者說多個任務。
3.5 管理器通過JMP指令切換到用戶任務
963 964 jmp far [es:ecx+0x14] ;執行任務切換3.6 用戶程序通過JMP指令切換到管理器
因為用戶任務是同一份代碼,所以也會執行到365行。不同的是這個任務不是嵌套于程序管理器的,所以會執行369行,用JMP指令返回。
3.7 程序管理器停機
966 mov ebx,prgman_msg3 967 call sys_routine_seg_sel:put_string 968 969 hlt這章的代碼就說到這里。感謝關注!
總結
以上是生活随笔為你收集整理的任务切换——《x86汇编语言:从实模式到保护模式》读书笔记38的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 适合程序员的四大字体
- 下一篇: 左右伸缩_SSFB梳齿型桥梁伸缩缝安装步