接下來將進入x86保護模式程式設計的一些細節。
從80386處理器開始,cpu有了四種執行模式
實模式保護模式
虛擬8086模式
smm模式工作模式是指cpu的定址方式、暫存器大小等。
cpu的實模式
在8086時代,cpu以實模式執行
cpu剛剛上電後,也以實模式啟動
參考資料:
(最開始的時候,cpu一共只有20位位址線、8個16位的段暫存器)
實模式下,記憶體定址方式由16位暫存器的內容乘以10h當作段基位址(由段暫存器提供),加上16位偏移位址形成20位實體地址(段基址:段偏移量)。最大定址空間1mb,最大分段64kb,可以使用32位指令。
段暫存器一共有6種:
cs、ds、ss、es、fs、gs
段暫存器
段暫存器是因為對記憶體的分段管理而設定的。計算機需要對記憶體分段,以分配給不同的程式使用。
在進行記憶體分段時,需要以下資訊:
段的大小
段的起始位址
段的屬性
需要使用8個位元組即64位儲存這些資訊
段暫存器只有16位,因此,段暫存器只能儲存segment selector,由其對映到存在記憶體中的全域性段號記錄表(gdt)來讀取段的資訊。
cs是**段。指向存放程式的記憶體。ip是存放下條待執行指令的偏移量,合在一起就是下條指令位置。
偏移量由通用暫存器提供,也是16位。
實體地址=段位址<<4+段內偏移
ds是資料段。預設。
ss是堆疊段。指向堆疊的記憶體段基位址,sp指向該堆疊的棧頂,合在一起可以訪問棧頂單元。加上bp可以訪問整個堆疊。
es是附加段。串操作指令中目的串所在的段。
將記憶體分段後,每個段都有乙個段基址,段暫存器儲存這個段基址的高16位,16位段基址左移4位後可以構成20位的段基址。
fs和gs沒有定義,由作業系統賦予它們目的。
實模式之所以稱之為實模式,是因為它的實體地址是真實的。
任意程式可以修改任意位址的變數
保護模式
隨著cpu的發展,實模式下的記憶體位址計算方式不再適用。
保護模式能提供更大的空間、更靈活、更安全的記憶體訪問。
記憶體定址方式需要相容老辦法,即(段基址、段偏移量)。
這個時候cpu內的通用暫存器全部換為32位,但是段暫存器還是16位的。
偏移值和實模式下一樣,仍然由通用暫存器提供,只是大小變成了32位。
將關於記憶體段的限制資訊放在gdt即全域性描述符表中,裡面的每乙個項稱之為段描述符。
段暫存器在保護模式下存放的東西類似於陣列索引。
使用專用暫存器gdtr指向全域性描述符表。
gdtr的結構:0-15存放gdt的長度,16-47存放基址。
乙個段描述符只能用來定義乙個記憶體段。
段界限:段邊界擴張法最值,即最大擴充套件多少、最小擴充套件多少。它的單位由g決定,g=1時單位為4kb,g=0時單位為位元組。
實際段界限邊界值 = (段界限+1) * 單位大小 - 1
若偏移位址超過段界限,則會報錯
此時的段暫存器內儲存的是乙個「選擇子」
選擇子的0-2位是rpl,描述特權級別
3位是ti,ti=0表示在gdt中索引,ti=1表示在ldt中索引
3-15位是描述符索引值
保護模式下,程式不能隨心所欲地修改任意位址的變數
虛擬8086模式
用於在保護模式下執行16位**
smm模式
不對程式設計師開放,不關心
cpu內部有5個32位的控制暫存器:cr0-cr3,cr8
cr0:含有控制cpu操作模式和狀態的標識
cr1:保留不用
cr2:儲存導致頁錯誤的線性位址
cr3:含有頁目錄表的物理記憶體基址
cr0中的保護控制位:pe
pe設定->開啟了保護模式
pg設定->開啟分頁機制(pe、pg都要設定)
cpu一進入保護模式立刻按照保護模式定址,這要求我們要在cpu進入保護模式前放好gdt
我們的核心剛剛啟動時,機器的狀態是這樣的:
eax 0x2badb002 732803074
ecx 0x1c300 115456
edx 0x2be00 179712
ebx 0x2bd20 179488
esp 0x7ffc 0x7ffc
ebp 0x0 0x0
esi 0x2be78 179832
edi 0x80 128
eip 0x10051f 0x10051f eflags 0x6 [ pf ]
cs 0x8 8
ss 0x10 16
ds 0x10 16
es 0x10 16
fs 0x10 16
gs 0x10 16
在實模式下,cpu只有1mb的定址空間,所以相當於gdt被限制在這裡了
grub在載入核心後,cs指向基位址為0x0、長度為4g-1的**描述符,ds指向了基位址為0x0、長度為1的資料段描述符
也就是,grub載入後,已經進入了平坦模式。
分段是intel的cpu一致保持的一種機制,分頁只是保護模式下的一種機制
如何繞過分段直接使用分頁?
使用平坦模式,即將整個記憶體作為乙個分段。
當整個虛擬空間為乙個起始位址為0、限長為4g的段時,給出的偏移位址就在數值上等於段機制處理後的位址了。
因此,此時計算機可以相容實模式
在grub引導後,我們的計算機已經進入了保護模式了
@ring: 0
cs = 0x8
ss = 0x10
ds = 0x10
es = 0x10
但是我們仍然需要再來一次,乙個是學習,另乙個是後期還要用到
首先需要構建資料型別
根據我們上面的介紹,構建如下的資料結構
// 定義gdt結構
typedef
struct
gdt_t;
typedef
struct
__attribute__
((packed)
)gdtr_t;
注意第二個結構體的__attribute__屬性
!特別重要
沒有加上這個屬性,結構體大小為64,加上這個屬性,結構體大小為48,差了特別多!
其次,讓我們構建初始化gdt的函式
#define gdt_len 5
static gdt_t gdt[gdt_len]
;static gdtr_t gdtr;
static
void
gdt_set
(int num, uint32_t base,
uint32_t limit, uint8_t access, uint8_t gran)
;void
init_gdt()
static
void
gdt_set
(int num, uint32_t base,
uint32_t limit, uint8_t access, uint8_t gran)
這裡的gdt_flush
負責更新gdrt
這個操作只能使用彙編完成
[global gdt_flush]
gdt_flush:
mov eax, [esp + 4]
lgdt [eax]
mov ax, 0x10
mov ss, ax
mov ds, ax
mov gs, ax
mov es, ax
mov fs, ax
jmp 0x08:.flush
.flush:
ret
注意這個jmp語句:
此處的jmp有兩個作用
重新整理cs暫存器
重新整理快取記憶體與流水線
這是nasm裡面的遠跳轉的寫法
寒假學習第四天
今天進行了spark的安裝。右擊,複製鏈結如下 在master的命令列中輸入 wget 複製結果如下 輸入命令 wget 2.2 解壓 輸入命令 tar zxf spark 2.4.0 bin hadoop2.7.tgz 2.3 遷移目錄 輸入命令 sudo mv spark 2.4.0 bin h...
第四天學習
一 權重關係 樣式表的權重關係 內聯樣式表的權重最大!內部和外部樣式的權重,和書寫的前後順序有關!放在後面的會把放在前面的樣式覆蓋掉 網頁的布局 先做上下排版 再做左右排版 從外往裡 二 css語法 選擇符選擇符 選擇符就是給標籤起名字 型別選擇符 標籤選擇符 所有的html標籤可以直接當做選擇符進...
C Primer學習第四天
第四章 陣列和指標 c 語言提供了兩種類似於vector和迭代器型別的低階復合型別 陣列和指標。與vector型別相似,陣列也可以儲存某種型別的一組物件 而它們的區別在於,陣列的長度是固定的。陣列一經建立,就不允許新增新的元素。指標則可以像迭代器一樣用於遍歷和檢查陣列中的元素。現代c 程式應盡量使用...