banner
Zein

Zein

x_id

x86架構與二進位介面的概述

amd64,intel64,x86-64,IA-32e ,x64 都被归为 64 位 x86 架构;IA-32 是 32 位 x86 架构

64bit os 啟用 x64 模式 (長模式):裝載 os 後,初始進入傳統的頁式地址保護模式,然後 PAE 模式被使能。
1)兼容子模式:允許運行 32bit 或 16bit 應用程序,而無需修改或重新編譯應用程序。然而虛擬 8086 模式、任務切換和堆疊參數複製特性在兼容模式下不被支持。因此在運行傳統應用程序時有一些限制。
2)64 位子模式:專門運行 64 bit 應用程序,支持 64 位線性地址空間。CPU 中的通用寄存器擴展到 64 位,並新增 8 個額外通用寄存器(R8~R15)。此外,還有新的 SIMD 擴展寄存器(XMM8~XMM15)來提高處理效率。64 位子模式還支持更高效的指令集,擴展的內存尋址,新的中斷優先級控制機制,提供更強大的運算能力。

32bit os 啟用 x86-32 模式:
1)實模式:邏輯地址直接映射物理地址,沒有分頁機制,20bit 外地址總線只能訪問 1MB 內存空間。BIOS 通常在這個模式下初始化硬件和數據結構。
2)保護模式:保護模式引入的機制是現代操作系統(如 Windows、Linux)運行的基礎。該模式下,CPU 可實現內存保護、虛擬內存、任務切換等功能,os 可利用這些功能管理硬件資源。系統可訪問更大的物理地址空間,且支持多任務和內存保護。
3)虛擬 8086 模式:允許在保護模式下運行 16bit 程序,這些程序可運行在近似於實模式的環境中。os 通過虛擬化技術將每個 16bit 程序運行在獨立的虛擬環境中,從而使其在不退出保護模式的情況下運行。

運行模式運行的子模式被多少位的 os 所啟用應用程序是否需重編譯默認地址長度默認操作數長度擴展寄存器通用寄存器位寬
x86-32 模式虛擬 8086 模式16bit不需要1616不使用16
實模式16bit 或 32bit不需要1616不使用16
保護模式16bit 或 32bit不需要16 或 3216 或 32不使用16 或 32
x64 模式 (長模式)64 位模式64bit需要6432使用64
兼容模式64bit不需要16 或 3216 或 32不使用16 或 32

下述描述以 x86-32 保護模式和 x64 兼容模式的經典架構為基礎脈絡,畢竟後續架構改進都以此為基礎;並會列出 x64 64 位模式的特殊改進

CPU 架構#

內存架構#

地址空間#

物理地址空間#

內存和其他硬件設備等 CPU 可以使用的資源統一編址為物理地址空間;CPU 用物理地址索引訪問。MMU 將 cpu 傳遞的虛擬地址轉換為物理地址後通過外地址總線訪問內存;外地址總線位數決定物理地址空間大小,外地址總線位數與內地址總線位數≥內地址總線位數;

內存管理單元 Memory Management Unit 有基址寄存器base:存儲進程空間 0 地址映射的物理地址;MMU 做如下工作:
physical address = virtual address + base;因為進程空間從零開始,而虛擬地址相當於進程空間的 offset

線性地址空間#

image

線性地址空間大小由 CPU 的內地址總線位數決定。內地址總線與 CPU 執行單元相連,內地址總線位數往往與 CPU 位數一致;線性地址空間描述的就是每個程序認為自己獨享的虛擬地址空間;線性地址空間會被映射到某一部分物理地址空間或整個物理地址空間。一个硬件平台上可以有多個線性地址空間;

分頁機制未啟用時,線性地址與物理地址相同;當 CPU 使用分頁機制時,需將線性地址轉換成物理地址才能訪問平台內存或其他硬件設備;

邏輯地址 / 段地址#

image

x86 早期分段機制的歷史包袱;邏輯地址是程序直接使用的地址;由 16bit 段索引子 + 32bit 或 64bit offset 構成。下述語句中的指針變量 p 存儲的就是變量 a 的邏輯地址的 offset,連接器和加載器會分配該 offset 所在的段索引子,CPU 從段寄存器或根據索引子查詢映射表 Descriptor table 獲得段對應的基址,段界限、權限等;然後將段基址 + offset 生成虛擬地址

int a = 100;
int *p = &a;

分段機制#

進程數據邏輯上劃分為代碼段、數據段和堆棧段等部分;程序用 Selector 標識每個邏輯段,用 offset 標識數據在段中的位置;

每個邏輯段映射到虛址空間段基址 Base + 段界限 Limit 描述的內存塊;程序訪問內存數據時,要把變量的邏輯地址 Selector+offset 轉換為線性地址 base+offset:
段寄存器緩存了 Selector→Descriptor 關係;Descriptor 中有段基址和各種屬性,權限標誌位;
1)進行屬性、訪問權限等檢查;
2)從 DS 數據段寄存器後半的 Descriptor 部分獲得該段映射的虛址空間基址 base;
3)將變量的 Segment offset 和該段在線性空間中的基址 base 相加,獲得該變量線性地址

Segment register 緩存未命中:
進程加載階段,在 LDT 的段映射關係數據結構創建好,且 LDT 地址在 GDT 註冊後,該進程 LDT 的 Selector 索引 GDT,獲得 LDT 的 Descriptor 描述的表地址,並將 Selector→Descriptor 關係緩存到 LDTR 寄存器。此外,該進程的 CS、DS、SS 被加載入相應 Selector,CPU 根據 Selector 的 TI 字段索引相應 Descriptor table,獲得相應 Descriptor,並將 Selector→Descriptor 關係緩存到 CS、DS、SS 對應的段寄存器

image

Descriptor#

image

Descriptor 描述段的基址、長度及各種屬性(如讀 / 寫、訪問權限等):
1)Base 字段描述該段基址
2)Limit 字段描述該段長度
3)DPL 字段表示代碼訪問此段所需最低權限

當 CPU 獲得段對應的 Descriptor 後,用 Descriptor 中各種屬性字段對訪問進行檢查,一旦確認訪問合法,CPU 將 Descriptor 中的 base(即 Segment 映射的虛址空間基址)和程序中邏輯地址的 offset 相加。獲得邏輯地址所對應線性地址。

Selector & GDT/LDT#

image

Selector:
程序只存儲和使用邏輯地址 offset 部分,Selector 是程序可見的邏輯地址組成部分,其修改和分配由連接器和加載器完成,用於索引 Descriptor table 獲得段對應 Descriptor:
1)Index:Descriptor table 的索引。
2)TI:指明索引 GDT 還是 LDT:
a. TI=0 時,表示索引 Global Descriptor Table;
b. TI=1 時,索引 Local Descriptor Table
3)RPL:Requested Privilege Level,所要求的權限級別。RPL 存在於段選擇寄存器的低 2 位,為程序訪問段時增加一級檢查。

GDT/LDT:
系統中至少有一個 global Descriptor table 可被所有的進程訪問。系統中可有一或多個 local Descriptor table,可被某進程私有,可被多進程共享;它們只是 Selector→Descriptor 的映射表,多數是線性表實現:
1)GDT 在內存中的位置由:基地址 Base 和表界限 Limit 描述。存放需多進程共享的段的映射關係,比如內核代碼段、共享庫段、內核數據段
2)LDT 存放不需進程間共享訪問的私有段的映射關係,比如用戶代碼段、用戶數據段、堆、棧等;LDT 在內存中的位置由 GDT 中的 Descriptor 描述,系統中有多少個 LDT,GDT 中有多少個對應 Descriptor
3)GDTR 和 LDTR 寄存器:這兩個寄存器幫助 cpu 快速找到 GDT/LDT 映射表:
a. GDTR:GDT 基址 Base + 表界限 Limit
b. LDTR:LDT 在內存中的位置由 GDT 中的 Descriptor 描述;Descriptor 會緩存到 LDTR;所以 LDTR 結構與段寄存器相同(Selector +Descriptor)

通過 Selector 索引 GDT/LDT 的過程如下圖所示:
Selector 的 TI 字段指明索引 GDT 還是 LDT;通過使用 LGDT/SGDT 指令對 GDTR 進行讀寫獲取映射表地址;在進行進程切換時,LDTR 的值會被換成新進程對應的 LDTR

image

Segment register#

每次都通過 Selector 索引 GDT/LDT 查表獲取邏輯段映射的虛址,這種類型的開銷優化:空間換時間,咱已經很熟悉了,x86 提供 6 個 16 位 Segment register,每個 Segment register 就是在 Selector 後加一個 Descriptor register 緩存邏輯段映射虛址 Descriptor:
1)CS(Code-Segment,代碼段):存放代碼段的 Selector→Descriptor
2)DS(Data-Segment,數據段):存放數據段的 Selector→Descriptor
3)SS(Stack-Segment,堆棧段):存放堆棧的 Selector→Descriptor
4~6)ES、FS、GS:可以存放額外三個數據段的 Selector→Descriptor,由程序自由使用。

當 Segment register 加載一個新的 Selector 後,CPU 自動將該 Selector 索引的 Descriptor 緩存到不可見的 Descriptor register 中,也就是說,CPU 只有在更新段寄存器時才索引 Descriptor table(比如切換綁定線程時會更新段寄存器)

image

分頁機制#

將虛擬地址空間分為多個頁,物理地址空間劃分也類似,減少了內存碎片;
分頁機制允許在物理內存告急時將不常用的頁移到磁碟的 swap space(如 Linux 的 Swap 分區,Windows 的虛擬內存文件),可認為是內存虛擬化機制的一項基礎。

32bit 內地址總線的虛址空間為可編址內存$2^{32}B=4\cdot2^{10}\cdot2^{10}\cdot2^{10}B=4GB$;(64bit 內地址總線使用其中 48 位可編址內存 256TB)
x86 架構允許大於 4KB 的頁大小(如 2MB、4MB);但 x86-32 頁的典型大小為 $4KB=4\cdot2^{10} B$,4GB 內存可劃分出 1024×1024 個頁。
可見頁表項數量與 $[內地址總線 (虛址空間大小) \cdot 進程數]$ 成正比

CPU 訪問數據流程:
CPU 訪問數據時,要將進程的虛擬地址(虛擬頁 VFN + 偏移 offset)轉換為實際物理地址(物理頁 PFN + 偏移 offset);
1)查找 TLB:CPU 首先在 TLB 中查找該虛擬地址的物理地址。如果 TLB 命中,直接使用此物理地址進行訪問。
2)查找 Page Table:如果 TLB Miss,CPU 根據虛擬地址中的各級頁表索引,查找相應頁表項。頁表項存儲著該虛擬頁對應的物理頁基址。
3)如果頁表項中沒有有效映射(如該頁不在物理內存中),觸發 Page Fault 中斷。os 將頁從磁碟的 swap space 加載到物理內存中,並更新 Page Table 相應頁表項 PTE,設置 P 位為 1,並對其他字段進行相應的設置;最後從缺頁錯誤處理程序中返回;CPU 重新查頁表,把對應的映射插入到 TLB 中
4)PFN+offset,得到對應物理地址

image

TLB#

Translation Lookaside Buffer 緩存最近用到的頁面映射(VFN→PFN),當 CPU 訪問某線性地址時,如果其所在頁的映射存在於 TLB 中,無須查頁表,即可得到該線性地址對應的 PFN,CPU 再將 PFN 與線性地址的偏移相加,得到物理地址。TLB 未命中則查詢 Page Table 並更新 TLB

Page Table#

image

Page Table 數據結構存儲 Virtual Frame Number→Physical Frame Number 的映射關係;出於 TLB 命中率和每次緩存的頁表大小考慮,Page Table 通常實現為多級頁表;

x86-32 保護模式下,Page Table 實現為兩級頁表;這樣每次緩存 TLB 時就不是緩存 1 張頁表中的 $2^{20}$ 個頁映射關係;而是緩存 $2^{10}$ 張頁表中的一張,每張 $2^{10}$ 個頁映射關係;CR3 指向一個 $2^{10}$ 個項的的頁目錄(每項 4B,頁目錄大小 4KB);每個頁目錄項又指向 1024 個 4KB 大小的頁表。未啟用 PAE 的 4KB 大小的頁面如圖所示。

image

4KB 頁大小只需 12bit 索引 page offset;規定 TLB 一次緩存 2102^{10} 210 個頁映射關係 PTE,即用 10bit 可編碼一次緩存的頁映射關係 PTE;剩下 10bit 自然編碼 2102^{10} 210 個二級 Page Table;
很明顯虛址的高 20bit,恰好編碼索引了整個二級頁表數據結構;
1)Page Table Entry:存儲 VFN→ PFN 映射關係。次 10bit 編碼索引了 2102^{10} 210 個 TLB 一次可緩存的頁映射關係,存放在二級 Page Table 中,從 PTE 中獲取 VFN→ PFN 映射關係,即可確定線性地址 VFN+offset 對應的物理地址 PFN+offset。

2)Page Directory Entry:高 10bit 編碼索引了所有二級 Page Table,即 PDE 存儲 10 位 2 進制編碼 xxxx xxxx xx → 二級 Page Table 基址的映射關係;所有 PDE 存儲在一級 Page Table;PDE 大小為 4B,頁目錄含 1024 個 PDE,占一個 4KB 的物理頁。

page fault#

PDE 和 PTE 都包含一個 P(Present)字段
1)P=1,物理頁在物理內存中,CPU 完成地址轉換後可直接訪問該頁。
2)P=0,物理頁不在物理內存,當 CPU 訪問該頁時,會產生 page fault 中斷並跳轉缺頁處理程序處理,os 通常將存放在 swap space 的頁調入物理內存,使訪問可繼續。由於程序的局部性特點,os 會將該頁附近的頁一起調入物理內存,方便 CPU 訪問。

物理地址擴展 PAE#

啟用 PAE 後,各級頁表大小仍是 4KB,但頁映射關係表數據結構實現為三級頁表;各級頁表項從 32 位擴為 64 位,以使用附加的地址位。拿出 2bit 編碼 1 級頁表;9bit 編碼 2 級頁表;9bit 編碼 3 級頁表;這樣,2,3 級頁表都只有 $2^{9}=512$ 個映射數據,是 2 級頁表方案的一半:而 CR3 指向 1 級頁表基址,1 級頁表是一個包含 4 個項的表。

image

PDBR/CR3 Register#

Page-Directory Base Register 或稱 CR3 Register 存放著頂級頁表的物理地址。一個進程在運行前,必須將其頂級頁表基地址存入 CR3,且頂級頁表基址必須對齊到 4KB 頁邊界

64 位子模式內存管理改進#

地址空間:
使用 64 位線性地址,但實際限制了有效虛擬地址空間為 48 位,高 48~63 位是相同的,程序可訪問 $2^{48} B=256TB$ 虛擬地址內存空間;對於物理地址空間,實際限制有效地址空間為 52 位,

分段機制未被禁用但作用被弱化:
cpu 不再用段基址進行地址轉換,CS、DS、ES、SS 等段寄存器基址置 0。FS 和 GS 段寄存器,仍存儲有段基址,可用於某些特定操作,如本地數據尋址和 os 內部數據結構管理(如線程局部存儲)。此外,段長度檢查被禁用,即 cpu 不會對段大小進行檢查。

分頁機制優化:
內存頁大小可以是 4KB、2MB、1GB。PAE 必需開啟,開啟後 os 使用四級頁表 —— 附加了一個第四級頁面映射表 Page-Map Level 4 Table,簡稱 PML 4 Table。幫助將 48 位線性地址轉換為 52 位物理地址。PML4 的物理基地址存在 CR3 中;
PML4 項:含一個 PML3 的物理基址、訪問權限和內存管理信息。
PML3 項:含一個 PML2 的物理基址、訪問權限和內存管理信息。
PML2 項:含一個 PML1 的物理基址、訪問權限和內存管理信息。
PML1 項:含一個 VDN→PFN、訪問權限和內存管理信息

image

中斷 & 異常架構#

子類別原因異步 / 同步返回行為舉例
中斷來自 I/O 設備的信號異步總是返回到下一條指令外部設備的響應請求,如敲擊鍵盤
異常錯誤潛在可恢復的錯誤同步可能返回到當前指令不一定是致命性錯誤,如缺頁錯誤
陷阱有意的異常同步總是返回到下一條指令請求系統調用,如文件讀取
終止不可恢復的錯誤同步不會返回致命性錯誤,如奇偶錯誤

中斷架構#

一些異常和中斷會打斷順序執行的指令流,轉而進入一條完全不同的執行路徑。現代計算機架構是由大量的中斷事件驅動的。中斷機制使外部硬件設備可以打斷 CPU 當前的執行任務,使 CPU 為自己提供服務;中斷從設備經由 “中斷控制器” 轉發給 CPU(MSI 除外)。

PIC(Programmable Interrupt Controller)#

可編程中斷控制器 PIC 是最早被廣泛應用的中斷控制器,PIC 只能在 UP(單處理器)平台上工作,無法用於 MP(多處理器)平台。具有 8 個中斷管腳(IR0~IR7),連接外部設備;當外部設備需要 CPU 處理時,通過對應中斷線發送信號,觸發中斷。

主要寄存器:
1)IRR(Interrupt Request Register): 記錄當前請求的中斷。如果外部設備發出 IR 中斷請求且該請求沒被屏蔽,在對應 IRR 中相應的位置置 1
2)ISR(In Service Register): 記錄當前正在處理的中斷。當 CPU 響應並開始處理中斷時,ISR 相應中斷位置 1。
3)IMR(Interrupt Mask Register): 屏蔽特定中斷線。如果 IMR 中對應中斷位置 1,對應 IR 管腳的中斷請求將被屏蔽,CPU 不會響應該中斷。

中斷處理流程:
1)外部設備發出中斷信號,如果該中斷沒有被屏蔽,IRR 中對應的位被置為 1。
2)PIC 通過 INT 管腳通知 CPU 有中斷發生。
3)CPU 通過 INTA(中斷應答)管腳響應 PIC,表示已收到中斷請求。
4)PIC 在收到 CPU 的應答後,會清除 IRR 中優先級最高的中斷請求,並將 ISR 相應的位設置為 1,表示中斷正在處理中。
5)CPU 再通過 INTA 發出第二次脈衝,PIC 會根據優先級提供相應的中斷向量並將其送到 CPU 的數據總線上。
6)CPU 處理完中斷後,通過寫 EOI(End of Interrupt)寄存器告知 PIC 中斷處理完成,PIC 會清除 ISR 中相應的位。

APIC(Advanced Programmable Interrupt Controller#

image

多處理器(MP)系統中,APIC 系統使得多個 CPU 能夠協調工作、共享中斷處理的任務。各 CPU 的 LAPIC 可以通過 IPI 機制進行通信和協作。這種中斷控制機制不僅支持高效的中斷處理,還能優化多核處理器系統的中斷平衡。

組成:
1)LAPIC (Local APIC):每個 CPU 內核都配備有一個 LAPIC,負責處理本地中斷信號。它通過 IRR(Interrupt Request Register)存儲當前中斷請求的狀態,ISR(Interrupt Service Register)存儲已處理的中斷標誌,CPU 通過寫 EOI(End of Interrupt)寄存器來告訴 LAPIC 該中斷已經處理完畢,允許其他中斷的處理。
2)IOAPIC (I/O APIC):通常位於南橋芯片(或稱低速 I/O 控制器中),負責接收外部設備中斷請求,並將它們轉發給特定的 CPU 的 LAPIC。IOAPIC 通常有多個中斷輸入管腳(通常為 24 個),每個管腳可以連接外部設備。

處理器間中斷(IPI):IPI (Inter-Processor Interrupt) 允許一個 CPU 向其他 CPU 發送中斷信號。這對於多核系統中的進程遷移、負載平衡和 TLB 刷新等操作非常重要。通過 LAPIC 的ICR(Interrupt Command Register),系統可以指定目標 CPU 並發送 IPI 中斷。

中斷傳遞過程:
1)當外部設備觸發中斷時,IOAPIC 接收到中斷請求信號。IOAPIC 使用 **PRT(Programmable Redirection Table)來查找中斷請求的路由信息。
2)根據 PRT 的配置,IOAPIC 將中斷信息格式化為
RTE(Redirection Table Entry)** 並傳遞給系統總線。
3)系統總線將中斷消息發送給目標 CPU 的 LAPIC。
4)LAPIC 收到中斷消息後,將根據 IRR 寄存器中的信息決定是否進行處理。如果該中斷符合處理條件,LAPIC 將觸發 ISR 寄存器,最終將中斷交給處理器。

異常架構#

中斷由外部設備產生,和 CPU 當前執行的指令無關。異常由 CPU 內部產生,其原因是 CPU 當前執行的指令出了問題。

異常產生原因按嚴重性劃分:
1)錯誤(Fault):由某種錯誤情況引起,一般可以被錯誤處理程序糾正。錯誤發生時,處理器將控制權交由對應的處理程序。前面所講的缺頁錯誤就屬於此類。
2)陷阱(Trap):指在執行了一條特殊指令後引起的異常。陷阱是有意的異常,陷阱最重要的用途是在用戶程序和內核之間提供一個像過程一樣的接口(即系統調用)。Linux 中用於實現系統調用的 INT 80 指令就屬於此類。
3)終止(Abort):指嚴重的不可恢復的錯誤,將導致程序終止。典型的是一些硬件錯誤。

IDT#

CPU 可通過中斷描述符表 Interrupt Descriptor Table 查找中斷或異常 vector 號對應的處理程序入口線性地址;
IDT 數據結構存儲 vector→Descriptor 的映射關係;Descriptor 中有處理例程的所在段的Selector和段內 offset,及各種屬性標誌位;
IDT 表基址存放在IDTR寄存器中:
1)Base:IDT 基址
2)Limit:IDT 表的最大字節數(通常為 8 字節對齊)

Interrupt Gate & Trap Gate#

Interrupt Gate 是 IDT 中的 Descriptor;描述發生特定中斷或異常時,應跳轉到哪個中斷服務例程 ISR。其格式與 segment Descriptor 類似 (但也有一些特殊屬性);為加以區分被稱為 System Descriptor,它的 S 位(Segment type)用於區分是 segment Descriptor 還是 System Descriptor
1)Offset:指向中斷服務例程 ISR 的地址;兩個 offset 分別表示高 16bit 和低 16bit
2)Selector:指明 ISR 所在的代碼段
3)Type:Interrupt Gate 類型,決定 Interrupt Gate 如何響應中斷
4)DPL(Descriptor Privilege Level):指示 Interrupt Gate 的特權級;中斷門和陷阱門的 DPL 只在使用 INT n 指令引起中斷或異常的時候才進行檢查,硬件產生的中斷或異常不檢查。
5)S 位(Segment Descriptor):控制該 Descriptor 是 segment Descriptor 還是 System Descriptor;
6)P (present):代表該中斷門是否有效,置 0 表示無效

Trap Gate 跟 Interrupt Gate 類似 (如下,格式完全一樣),但它用於異常處理。中斷門和陷阱門唯一的區別是程序通過中斷門跳轉後,EFLAGS 寄存器的 IF 位自動清零,中斷關閉。而陷阱門沒有這樣的效果。

I/O 架構#

計算機所處理的任務其實只有兩種:CPU 運算和 I/O 操作。I/O 是 CPU 訪問外部設備的方法。設備通常通過寄存器和設備 RAM 將自身功能展現給 CPU,CPU 通過讀 / 寫設備寄存器和 RAM 完成對設備的訪問及其他操作。

在現代計算機架構中,Port I/O 逐漸被淘汰,特別是在大多數 x86 系統中,MMIO 成為主流的 I/O 訪問方式,幾乎所有的外部設備(如顯卡、網卡、存儲控制器等)都通過 MMIO 進行通信。儘管 MMIO 提供更好的性能,但對於一些簡單的 I/O 設備(如低速的串行端口),Port I/O 可能仍有一定優勢,故還是介紹

Port I/O#

x86 為Port I/O分配了 65536 個 8bit 的 I/O 端口,Port I/O地址空間從 0x0000 到 0xFFFF,共 64KB。I/O 端口地址空間是獨立的,不是線性地址空間或物理地址空間的一部分。cpu 通過 I/O 端口地址訪問設備寄存器

訪問方法:cpu 執行IN指令將數據從指定的 I/O 端口讀取到寄存器中,而OUT指令則將數據從寄存器寫入指定的 I/O 端口。比如,IN AX, 0x60會將地址為0x60的 I/O 端口的數據讀入到 AX 寄存器中。

CPU 會通過一個特定的信號(比如 I/O 信號)來區分 I/O 操作和內存操作。2 個或 4 個連續的 8 位 I/O 端口可以組成 16 位或 32 位的 I/O 端口;
限制:端口 I/O 的最大缺點之一是速度相對較慢,因為每個 I/O 端口都要通過一個獨立的路徑進行訪問,不同設備可能需要通過中斷或輪詢來處理。這使得端口 I/O 在現代計算機系統中的使用逐漸被 MMIO 取代,尤其是在需要高效數據交換的場景中。

MM I/O#

  • *Memory map IO:** 設備寄存器或內存區域被映射到物理地址空間。通過內存訪問指令(如MOV)訪問設備寄存器或設備 RAM,而不需特殊的 I/O 指令。

由於現代 CPU 和系統都支持高效的內存訪問,MMIO 能夠提供比端口 I/O 更高的訪問速度和更大的帶寬。且由於它使用標準的內存訪問指令,簡化了編程和硬件設計。

訪問方法:如某設備的寄存器被映射到內存物理地址0xA0000,那麼執行MOV AX, [0xA0000]就可讀取該設備寄存器內容。

因 IO 寄存器狀態反應的是外部設備狀態,MMIO 地址區域通常不通過 CPU 的緩存進行優化,因為可能導致緩存不一致;尤其是不能緩存到 TLB(Translation Lookaside Buffer,地址轉換後備緩存)。這意味著 MMIO 操作的訪問可能會受到更高延遲的影響,尤其是在訪問頻繁的 I/O 設備時。

DMA#

直接內存訪問 Direct Memory Access 允許設備繞過 CPU 直接向內存中複製或讀取數據。如果設備向內存複製數據都經過 CPU,則 CPU 會有大量中斷負載,中斷過程中,CPU 對其他任務來講無法使用,不利於系統性能的提高。通過 DMA,CPU 只負責初始化,而傳輸動作由 DMA 控制器 DMAC 進行。

DMA 傳輸過程:
在實現 DMA 傳輸時,由 DMAC 直接控制總線,在 DMA 傳輸前,CPU 要把總線控制權交給 DMAC,結束 DMA 傳輸後,DMAC 立即把總線控制權交回給 CPU。
1)DMA 請求:CPU 對 DMAC 進行初始化,並向 I/O 端口發出操作命令,I/O 端口提出 DMA 請求。
2)DMA 響應:DMAC 對 DMA 請求進行優先級判別和屏蔽判別,然後向總線裁決邏輯提出總線請求。CPU 執行完當前總線周期後釋放總線控制權。此時,總線裁決邏輯發出總線應答,表示 DMA 已被響應,並通過 DMAC 通知 I/O 端口開始 DMA 傳輸。
3)DMA 傳輸:DMAC 獲得總線控制權後,CPU 即可掛起或只執行內部操作,由 DMAC 發出讀 / 寫命令,直接控制 RAM 與 I/O 端口進行 DMA 傳輸。
4)DMA 結束:當完成規定的成批數據傳送後,DMAC 釋放總線控制權,並向 I/O 端口發出結束信號。當 I/O 端口接收到結束信號後,停止 I/O 設備的工作並向 CPU 提出中斷請求,使 CPU 執行一段檢查本次 DMA 傳輸操作正確性判斷的代碼,並從不介入的狀態退出。

DMA 無須 CPU 直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢復現場的過程,通過硬件(DMAC)為 RAM 與 I/O 設備開辟了一條直接傳送數據的通路,極大地提高了 CPU 效率。需要注意的是,DMA 操作訪問的必須是連續的物理內存

image

時鐘#

os 中的很多事件都是由時鐘驅動的,如進程調度、定時器等。

1)周期性時鐘 Periodic Timer:最常見,時鐘以固定頻率產生時鐘中斷。周期性時鐘通常會有一個計數器,要麼以固定值遞減到 0 產生中斷,如 PIT(Programmable Interrupt Timer,可編程中斷時鐘);要麼固定增長,當達到某個閾值時產生中斷,同時自動將閾值增加一個固定值,計數器繼續遞增,如 HPET(High Precision Event Timer,高精度時間時鐘)。

2)單次計時器 One-shot Timer:大多數時鐘都可配置成這種方式,如 PIT、HPET。其工作方式和到達閾值產生中斷的周期性時鐘類似,只是產生中斷後閾值不會自動增加,而是需要時鐘中斷處理函數等軟件來增加該閾值。這為軟件提供了動態調整下一次時鐘中斷到來時間的能力。

x86 提供了多種時鐘,包括 PIT、RTC(Real Time Clock,實時鐘)、TSC(Time Stamp Counter,時間戳計數器)、LAPIC Timer 和 HPET 等。os 可根據需要使用其中的一種或多種時鐘,但是同時使用多個時鐘將帶來過多的時鐘中斷,從而影響系統的性能。在有高精度時鐘可用的時候,現代操作系統往往禁用低精度的時鐘,並根據需要使用高精度的時鐘模擬低精度的時鐘。

X64 Register#

general register#

image

兼容的 x86-32 通用寄存器在函數調用過程保存臨時變量及操作棧的指針等
eax算術累加器,通常用來執行加法,函數調用的返回值一般也放這
ebx數據存取
ecx通常用作計數器,如 for 循環
edxI/O 指針
esi字符串操作時,用於存放目的地址,和 edi 常搭配使用,執行字符串複製等操作
edi字符串操作時,用於存放數據源地址
esp堆棧指針寄存器 Stack Pointer:指向堆棧頂
ebp基址寄存器 Base Pointer:堆棧底指針,指向堆棧底,通常用 ebp + 偏移量的形式定位函數存放在堆棧中的局部變量
r8d~r15dx64 擴張的 8 個寄存器的低位

x64 架構中,上面的通用寄存器擴展為 64 位版本,名字也進行了升級。為兼容 32 位模式程序,使用上面的名字仍是可訪問的,相當於訪問 64 位寄存器的低 32 位。

64bit general register功能描述
rax通常用於存儲函數調用返回值
rsp堆棧頂指針,指向堆棧的頂部
rdi第一个入參
rsi第二個入參
rdx第三個入參
rcx第四個入參
r8第五個入參
r9第六個入參
rbx數據存儲,遵循 Callee Save 原則
rbp數據存儲,遵循 Callee Save 原則
r12~r15數據存儲,遵循 Callee Save 原則
r10~r11數據存儲,遵循 Caller Save 原則

寄存器傳參速度快,可減少對內存的讀寫次數。在編譯生成 CPU 指令時決定用堆棧還是用寄存器傳參;

32 位時代通用寄存器少,函數調用時,參數大多數時候是通過線程的堆棧來傳遞(也有用寄存器傳遞的,如 C++ this 指針使用 ecx 寄存器傳遞);

x64 時代,寄存器資源富裕了,參數傳遞絕大多數都用寄存器來傳。

status register/RFLAGS#

有翻譯為標誌寄存器的;裡面有眾多標記位,記錄了 CPU 執行指令過程中的一系列狀態,這些標誌大都由 CPU 自動設置和修改;x64 架構下,原來的 eflags 寄存器升級為 64 位的 rflags,不過其高 32 位並沒有新增什麼功能,保留為將來使用。

image

status register bit功能描述
CF進位標誌
PF奇偶標誌
ZF零標誌
SF符號標誌
OF補碼溢出標誌
TF跟蹤標誌
IF中斷標誌

instruction pointer/RIP#

指針 / 指令寄存器的;eip 存放下一條要執行指令地址,CPU 的工作就是不斷取出eip指向的指令,然後執行這條指令,同時指令寄存器繼續指向下一條指令,如此不斷重複。x64 架構下,32 位 eip 升級為 64 位的rip寄存器。
在漏洞攻擊中,黑客費盡心機修改指令寄存器存放的下一條指令地址,從而能夠執行惡意代碼。

這不就是 PC 指針嗎?

內存管理寄存器#

描述符表寄存器#

GDTRGDT 地址
LDTRLDT 地址

Segment registers#

Segment registers存儲邏輯段 selector→descriptor 映射
cs代碼段
ds數據段
ss堆棧段
es擴展段
fs數據段
gs數據段

控制 Register#

可用於管理和檢查 CPU 的狀態;這些寄存器決定了 CPU 運行的模式和特徵等。x86-64 的 64 位模式引入了 CR8,它被定義為任務優先級寄存器(TPR),操作系統能夠基於中斷的優先級別使用 TPR 來控制是否允許外部中斷來中斷處理器。

NamePurpose
CR0Basic CPU operation flags
CR1Reserved
CR2Page-fault linear address
CR3Virtual addressing state
CR4Protected mode operation flags
CR5Reserved
CR6Reserved
CR7Reserved
CR8Task priority register (TPR)
CR9Reserved
CR10Reserved
CR11Reserved
CR12Reserved
CR13Reserved
CR14Reserved
CR15Reserved

Debug registers/DR#

一組用於支持軟件調試的寄存器;程序能夠被調試,關鍵在於能夠被中斷執行和恢復執行,被中斷的地方就是我們設置的斷點。
解釋執行(PHP、Python、JavaScript)或虛擬機執行(Java)的高級語言,這很容易辦到,因為它們的執行都在解釋器 / 虛擬機的掌控之中。對 C、C++ 這樣的 “底層” 語言,代碼編譯成 CPU 機器指令執行,這就需要 CPU 提供調試支持

任務 Register#

Model Specific Register/MSR#

機器檢查寄存器#

性能監控寄存器#

內存區域類型寄存器(MTRR)#

流 SMID 擴張 (SSE) Register#

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。