天天看點

作業系統-第十五天 多任務(多程序)實作                                             多任務的實作過程

                                             多任務的實作過程

      終于等到第十五天-多程序的實作,在《30天自制作業系統》中介紹了多任務的實作,在此理清一下思路。

1.首先一個很重要的概念:在我們平常windows系統上我們可以同時做很多事(下載下傳,聽音樂,看視訊等),如果電腦是單核(一個CPU),那麼這些事并不是這個CPU在同時在做,而是CPU使用了一種障眼法(其實不是CPU使用的,而是我們寫的程式使用了障眼法,我們寫的程式讓CPU以一個極短的時間輪流執行着這些事,極短的時間讓肉眼看不見)。CPU在每個時刻隻能執行一件事。

2.那麼寫多程序的思路是什麼?

(1)首先我們先執行兩個程序(不讨論多程序),程序其實也就是一段可執行的程式,這段程式要在記憶體中才能執行,那麼放在記憶體的什麼地方呢?CPU又是怎麼找到這段程式呢?從這開始下手:

①在32位的保護模式下,我們通過 段選擇子+全局描述符表 進行記憶體的尋址(非常好的文章介紹記憶體尋址的文章http://blog.csdn.net/qq308845474/article/details/49054999)那麼我們就要在全局描述符表中選擇一段來寫入我們即将執行的程序的首位址。

#define ADR_GDT 0x00270000  //全局描述符表的開始位置
#define AR_TSS32 0x0089
struct SEGMENT_DESCRIPTOR *gdt=(struct SEGMENT_DESCRIPTOR *) ADR_GDT;
set_segmdesc(gdt+3,103,(int) &tss_a,AR_TSS32);	//第一個程序,這個地方gdt+3其實是bootpack.c的程式的位置
set_segmdesc(gdt+4,103,(int) &tss_b,AR_TSS32);	//這個是第二個程序,tss_a和tss_b可以先想成程式位址
           

      通過這個設定,就将這兩個程序的位置設定好,那麼CPU執行程序二的時候 ,先到GDT表中找到位置,如果執行指令 JMP FAR  0:3*8(CS置0,EIP置為3*8,那麼段基址+段内偏移=0+&tss_b),那麼CPU會找到程式的位置&tss_b。 CPU也是同樣找到程序一的位址,那麼至此程式的位址找到了。如果這樣想就錯了

(2)為什麼錯了?仔細想想如果程序一執行到一半,找到程序二的位址,切到程序二去執行了,那麼再切回到程序一,憑什麼CPU就知道到執行上次程序一切換的地方接着執行呢?那麼這就涉及到上面寫入全局描述符表裡的tss_a和tss_b了,這兩個并不是程序的入口位址,而是為了切換程序時儲存目前程序的所有程序的狀态的。

         TSS 全稱為task state segment,是指在作業系統程序管理的過程中,程序切換時的任務現場資訊。下面一段對TSS的介紹摘自http://blog.csdn.net/goodlixueyong/article/details/6018281 :  

      X86體系從硬體上支援任務間的切換。為此目的,它增設了一個新段:任務狀态段(TSS),它和資料段、代碼段一樣也是一種段,記錄了任務的狀态資訊。  

      與其它段一樣,TSS也有描述它的結構:TSS描述符表,它記錄了一個TSS的資訊,同時還有一個TR寄存器,它指向目前任務的TSS。任務切換的時候,CPU會将原寄存器的内容寫出到相應的TSS,同時将新TSS的内容填到寄存器中,這樣就實作了任務的切換。

TSS在任務切換過程中起着重要作用,通過它實作任務的挂起和恢複。所謂任務切換是指挂起目前正在執行的任務,恢複或啟動執行另一個任務。Linux任務切換是通過switch_to這個宏來實作的,它利用長跳指令,當長跳指令的操作數是TSS描述符的時候,就會引起CPU的任務的切換,此時,CPU将所有寄存器的狀态儲存到目前任務寄存器TR所指向的TSS段中,然後利用長跳指令的操作數(TSS描述符)找到新任務的TSS段,并将其中的内容填寫到各個寄存器中,最後,将新任務的TSS選擇符更新到TR中。這樣系統就開始運作新切換的任務了。由此可見,通過在TSS中儲存任務現場各寄存器狀态的完整映象,實作了任務的切換。 task_struct中的tss成員就是記錄TSS段内容的。當程序被切換前,該程序用tss_struct儲存處理器的所有寄存器的目前值。當程序重新執行時,CPU利用tss恢複寄存器狀态。

上面提到的TR寄存器是用來記住目前正在運作的任務,當任務切換時,TR的值會自動變化。我們可以通過LTR彙編指令來向TR寄存器寫入值。

  TSS的結構:

struct TSS32
{
	int backlink,esp0,ss0,esp1,ss1,esp2,ss2,cr3;
	int eip,eflags,eax,ecx,edx,ebx,esp,ebp,esi,edi;
	int es,cs,ss,ds,fs,gs;
	int ldtr,iomap;
};
           

設定到全局描述符中的tss_a和tss_b還沒有初始化。因為上述的程序一其實就是第一個開始的程序,是以tss_a在切換的時候會自動儲存資料到tss_a,是以不用初始化;現在對tss_b進行初始化,

tss_b_esp=(int) memman_alloc_4k(memman,64*1024)+64*1024-8;
	tss.esp=task_b_esp;	
	tss.eip=(int) &task_b_main;
	tss.cs=2*8;					
	tss.ds=1*8;
	tss.ss=1*8;
	tss.gs=1*8;
	tss.fs=1*8;
	tss.es=1*8;
           

重點是esp,eip和cs寄存器,因為這幾個寄存器關乎到程式的執行,因為第二個程序的代碼寫在bootpack.c中,二bootpack.c的代碼段是在GDT中的第三段,是以CS為2*8,段内偏移就是程序二的位址&task_b_main。這樣就會切換到第二個程序執行了。

那麼前面說的CPU會以極短的時間來輪流執行這些程序,這個結合定時器實作的,每個程序運作到定時器的一定時間,就自動切換到其他程序,這樣每個程序執行一段時間。

上面隻是本人看完《30天自制作業系統》和查閱一些資料的一些總結,其中有很多也是模模糊糊,在這隻是理一下思路。

繼續閱讀