關於段及相關概念的一些自己的闡述

1.
void sched_init(void)
{...
00392    

set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));

00393    

set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
...}
gdt:全局描述符,一共定義256個描述符,GDT本身不是一個段而是線形空間

的一個數據結構。GDT的基地址和長度需要加載進GDTR寄存器。GDT的基地

址要以8個字節對齊獲得最佳的運行效率。因爲GDT描述符號以8字節爲單位


2.結構相關解釋
typedef struct desc_struct {
     unsigned long a,b;
 } desc_table;
這裏使用兩個標準long四字節的存放gdt和ldt。
首先程序運行在虛擬地址上的(實際程序都如此在保護模式下)虛擬地址是

由兩部分組成 段選擇符號(16)+偏梁量(有效地址32位);通過段選擇符號來

訪問相應的段描述符號,由段描述符中提供的段基地址,限長,屬性及虛擬地

址另外的偏移來構造線形地址。
a.段選擇符:是虛擬地址組成部分見上面描述。其中前16位是段的唯一標誌

,並且提供了段描述符表GDT中的一個字段的偏移量。具體的組成如下面表

示:
|15-3描述符索引index|2是TI|1-0是RPL|
index:索引值用於尋找對應的描述符表中的具體段
TI: 表示是GDT還是LDT
RPL:類似權限的東西
Remark:段選擇符總共提供6個,CS DS SS ES FS GS,其實段描述符信息在訪

問的時候由cpu自動通過對16位段選擇符的解碼來加載到一個所謂的影子寄

存器中,這樣速度會非常快。可以直接使用當然對於我們用戶可能是看不到

這個寄存器的存在和操作。
b.段描述符表,如同gdt上面的也屬於段描述符表的一種,用於提供段選擇符

號的最終訪問獲得存儲的段信息。
c:最終的線形地址確立是由段描述符中提供的段基址+虛擬地址的段偏移。

然後再根據分頁的方法獲得最終的物理地址。

3.描述格式
//這個兩個低層調用及最後一個實現宏用於調用設置TSS,LDT段描述孵內容


//n:爲描述符的地址,即後面會往n開始到n+7位置中寫入一個完整的8個字

節的段描述符。
//add:基地址,即段信息中所保存的段基址
//0x8X是屬性描述表示是何種類型的段TSS or LDT or the other
#define  set_tss_desc(n, addr)   _set_tssldt_desc(((char *)

(n)),((int)(addr)),"0x89")
#define  set_ldt_desc(n, addr)   _set_tssldt_desc(((char *)

(n)),((int)(addr)),"0x82")
段描述符的格式
高32位中
|31-24:基地址

31..24|23:G|22:D/B|21:0|20:AVL|19-16:Limit19..16|15:P|14-13:DPL|1

2:S|11-8:TYPE|7-0:基地址23..16|
低32位中
|31-16:base address 15..0|15-0:Segment Limit|

4.
//看具體實現加載段描述符的過程
#define _set_tssldt_desc  (  n,  addr,  type)
//104是段限長,TSS最長104個字節放到低2個字節 15-0:Segment Limit
__asm__ ("movw $104,%1/n/t" /
//ax段基地址的低16位放入 31-16:base address 15..0
    "movw %%ax,%2/n/t" /
//右移16位 ax中變成高16位地址
    "rorl $16,%%eax/n/t" /
//將高16位地址中的低8位放入 7-0:基地址23..16
    "movb %%al,%3/n/t" /
//將type放入|15:P|14-13:DPL|12:S|11-8:TYPE
    "movb $" type ",%4/n/t" /
//|23:G|22:D/B|21:0|20:AVL|19-16:Limit19..16|全部置零
    "movb $0x00,%5/n/t" /
//高基地址8位放入最高位
    "movb %%ah,%6/n/t" /
    "rorl $16,%%eax" /
    //輸入規則%0指定eax存放addr段基址,%1~%6均爲指定m內存地址分別

爲n,n+2,n+4...地址內容
    ::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), /
     "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) /
    )
5.加載任務TSS,LDT等段選擇符和段描述符加載到任務寄存器中的實現
n:第n號任務或局部表的段偏移
//ltr或者lldt命令加載%%ax的段選擇符,此段選擇可以看到是由低層的一

個宏_TSS和_LDT來返回來實現。
#define ltr  (n)     __asm__("ltr %%ax"::"a" (_TSS(n)))
#define lldt (n)     __asm__("lldt %%ax"::"a" (_LDT(n)))
//_TSS _LDT將返回一個當前任務信息或者局部描述符的對應段偏移是相對

於GDT中的偏移,這裏使用一個簡單的計算公式 n*(8+8)+起始偏移數目*8,

這裏我們要看下這些TSS和LDT在GDT中位置。
#define _TSS  (n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
#define _LDT  (n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
6.GDT具體分佈
0 |NULL   |  不可以使用的GDT(0)
1 |系統代碼段CS |  操作系統的自己使用
2 |系統數據段DS |  操作系統的數據段
3 |系統調用 |  一些調用的信息符
4 |TSS0  |  這裏很容易看到FIRST_TSS_ENTRY=4
5 |LDT0  |  這裏很容易看到FIRST_LDT_ENTRY=5
....
每個域都是8個字節所以第n個TSS地址 = TSS0的基址+ (n*LSSi-LSSi-1)
= FIRST_TSS_ENTRY*8 + n*(8+8)
= FIRST_TSS_ENTRY<<3+n<<4
同理可以推導出LDT的地址進行加載

至此基本清楚段選擇符 描述符 段一些關係和如何設置的操作
#define str  (n)  
__asm__("str %%ax/n/t" /
    "subl %2,%%eax/n/t" /
    "shrl $4,%%eax" /
    :"=a" (n) /
    :"a" (0),"i" (FIRST_TSS_ENTRY<<3))

 

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