首先對某些概念進行解釋。linux
數據總線:是計算機中各個組成部件間進行數據傳輸時的公共通道;「內數據總線寬度」是指CPU芯片內部數據傳送的寬度;「外數據總線寬度」是指CPU與外部交換數據時的數據寬度。顯然,數據總線位數越多,數據交換的速度就越快。數組
地址總線:是載對存儲器或I/O端口進行訪問時,傳送由CPU提供的要訪問的存儲單元或I/O端口的地址信息的總線,其寬度決定了處理器能直接訪問的主存容量的大小。數據結構
如今的微型計算機系統採用下圖的三級存儲器組織結構,即緩衝存儲器Cache、主存、和外存。架構
高速緩衝存儲器Cache的使用,大大減小了CPU讀取指令和操做數所需的時間,使CPU的執行速度顯著提升。spa
在80X86CPU的發展過程當中,存儲器的管理機制發生了較大的變化。8086/8088CPU對存儲器的管理採用分段的實方式;80286CPU除了可在實方式下工做外,還能夠在保護模式下工做;而80386CPU以後的處理器則具備三種工做方式:實方式、保護方式和虛擬8086方式。.net
三種工做方式的轉換如圖:設計
80386三種工做方式之間的轉換unix
8086寄存器結構:指針
8086/8088CPU內部有14個16位寄存器。按功能分爲三大類:通用寄存器(8個)、控制寄存器(2個)、段寄存器(4個)。code
通用寄存器包括4個數據寄存器、兩個地址寄存器和兩個變址寄存器。
數據寄存器:用於存放參與運算的操做數或運算結果。包括AX累加器、BX基址寄存器、CX計數寄存器、DX數據寄存器。
指針寄存器:SP堆棧指針寄存器、BP基址寄存器。
變址寄存器:SI源變址寄存器、DI目的變址寄存器。
段寄存器包括:CS代碼段寄存器、DS數據段寄存器、SS堆棧段寄存器、ES附加數據段寄存器。
控制寄存器包括:IP指令指針寄存器、FLAGS狀態標誌寄存器。
段的引入:8086爲了用16位寄存器實現1MB的尋址內存空間,引入了段的概念。
段是虛擬地址空間的基本單位,分段機制把虛擬地址空間的一個地址轉換爲線性地址空間的一個線性地址。
虛擬地址到線性地址的轉換
虛擬地址到線性地址的映射
其中base爲段的基地址,是線性地址空間中段的起始地址
limit是段的界限,在虛擬空間中,段內可使用的最大偏移量
固然虛擬地址到線性地址的映射經過段寄存器和段描述符表共同完成。且載保護模式下段描述符表分爲全局描述符表(GDT)、中斷描述符表(IDT)、及局部描述符表(LDT)。Linux 中的段控制單元
在 Linux 中,全部的段寄存器都指向相同的段地址範圍 —— 換言之,每一個段寄存器都使用相同的線性地址。這使 Linux 所用的段描述符數量受限,從而可將全部描述符都保存在 GDT 之中。這種模型有兩個優勢:
· 當全部的進程都使用相同的段寄存器值時(當它們共享相同的線性地址空間時),內存管理更爲簡單。
· 在大部分架構上均可以實現可移植性。
Linux 使用如下段描述符:
· 內核代碼段
·內核數據段
·用戶代碼段
·用戶數據段
·TSS 段
·默認 LDT 段
GDT 中的內核代碼段 (kernel code segment)描述符中的值以下:
· Base = 0x00000000
· Limit = 0xffffffff (2^32 -1) = 4GB
· G(粒度標誌)= 1,表示段的大小是以頁爲單位表示的
· S = 1,表示普通代碼或數據段
·Type = 0xa,表示能夠讀取或執行的代碼段
· DPL 值 = 0,表示內核模式
與這個段相關的線性地址是 4 GB,S = 1 和 type = 0xa 表示代碼段。選擇器在cs寄存器中。Linux 中用來訪問這個段選擇器的宏是_KERNEL_CS。
內核數據段 (kernel data segment)描述符的值與內核代碼段的值相似,唯一不一樣的就是 Type 字段值爲 2。這表示此段爲數據段,選擇器存儲在ds寄存器中。Linux 中用來訪問這個段選擇器的宏是_KERNEL_DS。
用戶代碼段 (user code segment)由處於用戶模式中的全部進程共享。存儲在 GDT 中的對應段描述符的值以下:
· Base = 0x00000000
· Limit = 0xffffffff
· G = 1
· S = 1
·Type = 0xa,表示能夠讀取和執行的代碼段
· DPL = 3,表示用戶模式
在 Linux 中,咱們能夠經過_USER_CS宏來訪問此段選擇器。
在用戶數據段 (user data segment)描述符中,唯一不一樣的字段就是 Type,它被設置爲 2,表示將此數據段定義爲可讀取和寫入。Linux 中用來訪問此段選擇器的宏是_USER_DS。
除了這些段描述符以外,GDT 還包含了另外兩個用於每一個建立的進程的段描述符 —— TSS 和 LDT 段。
每一個 TSS 段 (TSS segment)描述符都表明一個不一樣的進程。TSS 中保存了每一個 CPU 的硬件上下文信息,它有助於有效地切換上下文。
每一個進程都有本身在 GDT 中存儲的對應進程的 TSS 描述符。這些描述符的值以下:
· Base = &tss (對應進程描述符的 TSS 字段的地址;例如 &tss_struct)這是在 Linux 內核的 schedule.h 文件中定義的
· Limit = 0xeb (TSS 段的大小是 236 字節)
· Type = 9 或 11
· DPL = 0。用戶模式不能訪問 TSS。G 標誌被清除
全部進程共享默認 LDT 段。默認狀況下,其中會包含一個空的段描述符。這個默認 LDT 段描述符存儲在 GDT 中。Linux 所生成的 LDT 的大小是 24 個字節。
全部GDT存放在cpu_gdt_table[]數 組中,段的大小和指針存放在cpu_gdt_descr[]數組中。
Linux中的段:如今的大多數硬件平臺都不支持分段機制,可是又不能繞過她而直接給出線性地址空間,在這種狀況下,linux的設計人員巧妙的讓段的基地址爲0,使得偏移量=線性地址。從而實現了虛擬地址到線性地址的映射。
分頁機制的引入:段機制規定,必須爲代碼段和數據段建立不一樣的段,這樣一來,不一樣的特權級都出現了相應的CS和DS,這樣就失去了段保護的做用。
分頁機制完成了線性地址到物理地址的轉換過程。
首先了解分頁機制以前,先要分清頁和頁面的概念:
頁:將線性地址空間劃分紅若干大小相等的片,稱爲頁(page)
頁面:對應頁的劃分將物理地址空間分紅與頁大小相等的若干存儲塊,就稱爲頁面或塊。
咱們知道線性地址空間的大小爲4GB,那怎樣來劃分頁的大小合理呢?IA32的標準頁大小爲4KB,也就是說,線性地址空間被分紅1M個4KB大小的頁。分大點能夠不?能夠,可是相應的內存中頁面的數目就減小,帶來的結果是一個頁面的空間過大,那麼對於一個小數據來講,給他分配一個塊是否是有點浪費?要不分小點,在內存運行時數據被分散到了不一樣的塊,讀起來也是很費勁的,所以,就巧妙的把頁的大小設爲4KB。
分頁機制中使用頁表這種數據結構實現了線性地址到物理地址的映射。
頁表中包含物理頁面基地址和頁的屬性。
對於頁表有兩級頁表和三級頁表之分。linux爲了保證可移植行,
採用了三級分頁機制,固然其在某些狀況下能夠返回到二級分頁。
上圖爲線性地址到物理地址的映射