CPU实模式和保护模式、全局描述符表GDT、Linux内核中GDT和IDT的结构定义
一 計(jì)算機(jī)實(shí)模式和保護(hù)模式
?
實(shí)模式
 在實(shí)模式下,內(nèi)存被限制為僅有1M字節(jié)(220 字節(jié))。有效的地址從00000到FFFFF (十六進(jìn)制)。
 這些地址需要用20位的數(shù)來(lái)表示。一個(gè)20位的數(shù)不適合任何一個(gè)8086的16位寄存器。
 Intel通過(guò)利用兩個(gè)16位數(shù)值來(lái)決定一個(gè)地址的方法來(lái)解決這個(gè)問(wèn)題。開始的16位值稱為段地址(selector)。
 段地址的值必須存儲(chǔ)在段寄存器中。第二個(gè)16位值稱為偏移地址(offset)。
16位保護(hù)模式
 在實(shí)模式下,一個(gè)段地址的值是物理內(nèi)存里的一節(jié)的首地址。在保護(hù)模式下,一個(gè)段地址的值是一個(gè)指向描述符表的指針
 。兩種模式下,程序都是被分成段。在實(shí)模式下,這些段在物理內(nèi)存的固定位置而且段地址的值表示段開始處所在節(jié)的首
 地址。在保護(hù)模式下,這些段不是在物理內(nèi)存的固定的地址。事實(shí)上,它們不一定需要在內(nèi)存中。
保護(hù)模式使用了一種叫做虛擬內(nèi)存的技術(shù)。虛擬內(nèi)存的基本思想是僅僅保存程序現(xiàn)在正在使用的代碼和數(shù)據(jù)到內(nèi)存中。其
 它數(shù)據(jù)和代碼暫時(shí)儲(chǔ)存在硬盤中直到它們?cè)俅涡枰獣r(shí)。
在保護(hù)模式下,每一段都分配了一條描述符表里的條目。這個(gè)條目擁有系統(tǒng)想知道的關(guān)于這段的所有信息。這些信息包括
 :現(xiàn)在是否在內(nèi)存中;如果在內(nèi)存中,在哪;訪問(wèn)權(quán)限(例如: 只讀)。段的條目的指針是儲(chǔ)存在段寄存器里的段地址值
 。
32位保護(hù)模式
 80386引入了32位保護(hù)模式。
 386 32位保護(hù)模式和286 16位保護(hù)模式之間最主要的區(qū)別是:
 ? ? 1 偏移地址擴(kuò)展成了32位。這就允許偏移地址范圍升至4G。因此,段的大小也升至4G。
 ? ? 2 段可以分成較小的4K大小的單元,稱為內(nèi)存頁(yè)。虛擬內(nèi)存系統(tǒng)工作在頁(yè)的方式下,代替了段方式。這就意味著一段
 在任何一個(gè)時(shí)刻只有部分可能在內(nèi)存中。在28616位保護(hù)模式下,要么整個(gè)段在內(nèi)存中,要么整個(gè)不在。?
? ? 在Windows 3.x系統(tǒng)中,標(biāo)準(zhǔn)模式為286 16位保護(hù)模式而增強(qiáng)模式為32位保護(hù)模式。Windows 9X,Windows
 NT/2000/XP,OS/2和Linux都運(yùn)行在分頁(yè)管理的32位保護(hù)模式下。
?
二 全局描述符表GDT
?
? ? GDT,Global Descriptor Table。
 ? ? IA32允許將一個(gè)段的基地址設(shè)置為32bit所能表示的任何值,limit(段大小)則可以設(shè)置成以2^12為倍數(shù)的任何值。
 ? ? 在保護(hù)模式下,對(duì)一個(gè)段的描述包括以下三個(gè)方面:[Base Address,Limit,Access],他們加在一起被放在一個(gè)64bit
 長(zhǎng)的數(shù)據(jù)結(jié)構(gòu)中,被成為段描述符。
 ? ? 在這種情況下,如果我們直接通過(guò)一個(gè)64bit段描述符來(lái)引用一個(gè)段的時(shí)候,就必須使用一個(gè)64bit長(zhǎng)的段寄存器裝入
 這個(gè)段描述符。
 ? ? 把這些長(zhǎng)度為64bit的段描述符放入一個(gè)數(shù)組中,而將段寄存器中的值作為下標(biāo)索引來(lái)間接引用(事實(shí)上就是將段寄
 存器中的高13bit的內(nèi)容作為索引)。這個(gè)全局?jǐn)?shù)據(jù)就是GDT。
?
三 Linux中GDT和IDT的結(jié)構(gòu)定義
?
GDT,IDT都是全局的。LDT是局部的(在GDT中有它的描述符);
GDT用來(lái)存儲(chǔ)描述符(門或非門);系統(tǒng)中幾個(gè)CPU,就有幾個(gè)GDT
struct gdt_page {
? ? struct desc_struct gdt[GDT_ENTRIES];
} __attribute__((aligned(PAGE_SIZE)));
 DECLARE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page);
 IDT整個(gè)系統(tǒng)只有一個(gè);
 系統(tǒng)啟動(dòng)時(shí)候需要初始化GDT和IDT;
 描述符結(jié)構(gòu)定義,在<arch/x86/include/asm/desc_defs.h>
struct desc_struct {
? ? union {
? ? ? ? struct {
? ? ? ? ? ? unsigned int a;
? ? ? ? ? ? unsigned int b;
? ? ? ? };
? ? ? ? struct {
? ? ? ? ? ? u16 limit0;
? ? ? ? ? ? u16 base0;
? ? ? ? ? ? unsigned base1: 8, type: 4, s: 1, dpl: 2, p: 1;
? ? ? ? ? ? unsigned limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8;
? ? ? ? };
? ? };?
 } __attribute__((packed));
上面第一個(gè)匿名結(jié)構(gòu)體用來(lái)作為成員訪問(wèn)取值的出口,下面第二個(gè)結(jié)構(gòu)體對(duì)真實(shí)的成員設(shè)置值的入口。
字段:
limit:段長(zhǎng)度
base:段的首字節(jié)的線性地址,有base0,base1,base2三部分構(gòu)成
type:段的類型和存取權(quán)限
s:系統(tǒng)標(biāo)志。1-系統(tǒng)段;0-普通段
dpl:描述符特權(quán)級(jí)
p:segment-Present。linux下總是1
avl:linux不用
d:區(qū)分代碼段還是數(shù)據(jù)段
g:段大小粒度。以4K倍數(shù)計(jì)算
?
在32位機(jī)器上,這就是所有描述符的數(shù)據(jù)結(jié)構(gòu);
typedef struct desc_struct gate_desc;
typedef struct desc_struct ldt_desc;
typedef struct desc_struct tss_desc;
?
由于三類描述符都是一個(gè)結(jié)構(gòu)類型,從而一律使用下面宏初始化在GDT中表項(xiàng)
#define GDT_ENTRY_INIT(flags, base, limit) { { { \
? ? ? ? .a = ((limit) & 0xffff) | (((base) & 0xffff) << 16), \
? ? ? ? .b = (((base) & 0xff0000) >> 16) | (((flags) & 0xf0ff) << 8) | \
? ? ? ? ? ? ((limit) & 0xf0000) | ((base) & 0xff000000), \
? ? } } }
?
但是在64位機(jī)器上,Linux則進(jìn)行了細(xì)致劃分;
 無(wú)論是32位還是64位機(jī)器上,都使用typedef重新定義,以提供給系統(tǒng)其他使用此描述符的部分一致的類型名;
?
區(qū)分描述符的枚舉量
enum {
? ? GATE_INTERRUPT = 0xE,
? ? GATE_TRAP = 0xF,
? ? GATE_CALL = 0xC,
? ? GATE_TASK = 0x5,
};
 enum {
? ? DESC_TSS = 0x9,
? ? DESC_LDT = 0x2,
? ? DESCTYPE_S = 0x10, ?/* !system */
};
?
系統(tǒng)GDT,IDT指針描述結(jié)構(gòu)
struct desc_ptr {
? ? unsigned short size;
? ? unsigned long address;
} __attribute__((packed)) ;
 這個(gè)結(jié)構(gòu)記錄了系統(tǒng)的GDT或者IDT的大小以及在系統(tǒng)中的線性基址;
Reference:
? ? <arch/x86/include/asm/desc_defs.h>
?
四 如何通過(guò)段描述符訪問(wèn)內(nèi)存
?
當(dāng)我們要訪問(wèn)某個(gè)段中的一個(gè)地址時(shí)候:
 ? ? 1 從GDTR中拿到GDT在內(nèi)存中的基地址,得到段描述符表;?
? ? 2 從段選擇子中的前13位得到我們要訪問(wèn)的段的描述符在段描述符表中的索引;?
? ? 3 從段描述符表中得到要訪問(wèn)的段的描述符,得到其基地址;
 ? ? 4 基地址加上偏移地址就是我們要訪問(wèn)的內(nèi)存地址(這里是虛擬地址,接下來(lái)是分頁(yè)機(jī)制的功能將虛地址轉(zhuǎn)換為物理
 地址,)
?
看內(nèi)核相關(guān)源碼涉及到 AT&T匯編,和Intel匯編語(yǔ)法不同;先學(xué)部分AT&T匯編語(yǔ)法;
? ? 操作數(shù)排列是從源(左)到目的(右),如"movl %eax(源), %ebx(目的)";
? ? 符號(hào)常數(shù)直接引用 如:
 value: .long 0x12a3f2de
 movl value , %ebx
? ? 會(huì)見到如下的指令:push,pushl,pushfl;
? ? 操作數(shù)的長(zhǎng)度用加在指令后的符號(hào)表示b(byte, 8-bit), w(word, 16-bits), l(long, 32-bits);
 ? ? 如
 ? ? ? ? "movb %al, %bl",
 ? ? ? ? "movw %ax, %bx",
 ? ? ? ? "movl %eax, %ebx",
 ? ? 目前還不知道pushfl是啥;
? ? 如果沒有指定操作數(shù)長(zhǎng)度的話,編譯器將按照目標(biāo)操作數(shù)的長(zhǎng)度來(lái)設(shè)置。
參閱
 https://blog.csdn.net/cwcmcw/article/details/21640363
 http://blog.chinaunix.net/uid-29113598-id-5210949.html
總結(jié)
以上是生活随笔為你收集整理的CPU实模式和保护模式、全局描述符表GDT、Linux内核中GDT和IDT的结构定义的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: 中断描述符表IDT以及Linux内核ID
- 下一篇: Linux内核进程管理基本概念-进程、运
