MBR引導加載器:實模式硬盤讀取與操作系統(tǒng)啟動原理)
1. 項目概述從“能啟動”到“能干活”的MBR如果你跟著《操作系統(tǒng)真象還原》這本書走到了第三章恭喜你你已經(jīng)成功讓計算機從硬盤上讀取了你的代碼并執(zhí)行了。屏幕上那個小小的“1 MBR”或者你自定義的字符串就是MBR主引導記錄生命最初的啼哭。但興奮過后一個現(xiàn)實的問題立刻擺在眼前這個只有512字節(jié)的小程序除了打印點字符還能做什么它離一個真正的“引導程序”還差得遠。第三章“完善MBR”的核心任務就是把這塊512字節(jié)的“彈丸之地”從一個簡單的演示程序升級為一個具備初步工作能力的“引導加載器”雛形。簡單來說前兩章我們造了一個能響應的“空殼”第三章我們要往這個殼子里注入靈魂——讓它具備從硬盤加載其他程序比如后續(xù)的操作系統(tǒng)內(nèi)核加載器的能力。這就像你造了一艘小艇MBR之前只驗證了它能浮在水上能被BIOS加載執(zhí)行現(xiàn)在你要給它裝上槳和導航系統(tǒng)讓它能駛向遠方的大船內(nèi)核并將其接過來。這個過程涉及對計算機底層硬件如硬盤端口、內(nèi)存布局的精確操作是打通從“固件”到“軟件”的關(guān)鍵一步。無論你是想深入理解操作系統(tǒng)啟動的每一個齒輪如何咬合還是立志于動手實現(xiàn)自己的迷你系統(tǒng)這一章都是無法繞過的硬核實踐。2. 核心需求解析為什么MBR需要“完善”一個“完善”的MBR其使命遠不止于向屏幕輸出信息。在傳統(tǒng)的Legacy BIOS啟動模式下MBR是上電自檢POST后由BIOS加載到內(nèi)存并執(zhí)行的第一段“軟件”。它被賦予了幾項關(guān)鍵職責而原書前兩章的示例僅僅完成了最微不足道的一小部分。2.1 核心職責一探查與規(guī)劃內(nèi)存BIOS將MBR加載到物理地址0x7c00處并跳轉(zhuǎn)執(zhí)行。此時MBR對系統(tǒng)的了解幾乎為零。它需要知道哪些內(nèi)存區(qū)域是可用的、哪些是被系統(tǒng)保留的比如用于顯存、BIOS數(shù)據(jù)區(qū)等。雖然在本章的具體實現(xiàn)中我們可能暫時簡化這一步但一個健壯的引導程序必須具有內(nèi)存探測的能力以便為后續(xù)加載的代碼安排安全的落腳點。理解內(nèi)存布局是避免程序間互相覆蓋、導致系統(tǒng)崩潰的基礎(chǔ)。2.2 核心職責二加載后續(xù)的引導程序或內(nèi)核加載器512字節(jié)的容量扣除引導簽名和分區(qū)表留給代碼的空間大約只有440字節(jié)左右。這點空間連一個稍微復雜點的顯示器模式設置都做不完更別提加載現(xiàn)代操作系統(tǒng)內(nèi)核了。因此MBR的核心任務就是作為“第一級引導加載器”從硬盤上找到并加載體積更大、功能更強大的“第二級引導加載器”比如GRUB的core.img或者你自己寫的loader.bin。這個過程就是“加載加載器”是啟動鏈條中承上啟下的關(guān)鍵一環(huán)。2.3 核心職責三傳遞必要的硬件信息BIOS在啟動過程中檢測并初始化了硬件這些信息如檢測到的內(nèi)存大小、顯卡模式等通常保存在特定的內(nèi)存區(qū)域。一個完善的MBR在跳轉(zhuǎn)到第二級加載器之前可以將這些信息的指針或數(shù)據(jù)本身傳遞過去為后續(xù)階段節(jié)省重新探測硬件的開銷。這需要遵循一定的約定例如通過寄存器或特定的內(nèi)存地址來傳遞參數(shù)。2.4 核心職責四完成必要的早期硬件初始化在某些場景下MBR可能還需要進行一些最基本的硬件設置。最常見的就是將處理器從實模式16位切換到保護模式32位甚至長模式64位的前期準備。雖然完整的切換通常由第二級加載器完成但MBR可能需要準備好全局描述符表GDT等數(shù)據(jù)結(jié)構(gòu)。本章的“完善”主要聚焦于第二個職責——實現(xiàn)從硬盤加載數(shù)據(jù)到內(nèi)存這一核心功能這是后續(xù)所有高級功能的基礎(chǔ)。3. 技術(shù)選型與設計思路在實模式下與硬盤對話要在實模式下從硬盤加載數(shù)據(jù)我們面臨幾個關(guān)鍵選擇。這些選擇決定了代碼的復雜度、兼容性和可靠性。3.1 訪問方式CHS vs LBACHS柱面-磁頭-扇區(qū)這是古老的硬盤尋址方式直接對應硬盤的物理結(jié)構(gòu)。需要知道磁頭數(shù)、柱面數(shù)、每磁道扇區(qū)數(shù)。計算復雜且對于容量大于8GB的硬盤存在尋址限制24位CHS尋址上限。但它的兼容性最好是所有硬盤都支持的基礎(chǔ)方式。LBA邏輯塊地址將整個硬盤扇區(qū)線性編號從0開始。操作簡單直觀是現(xiàn)代操作系統(tǒng)和硬盤的主要尋址方式。但需要硬盤控制器支持LBA模式。設計決策對于學習性和兼容性優(yōu)先的引導程序通常先實現(xiàn)CHS讀取作為基礎(chǔ)。因為即使在最老的機器上BIOS也會提供CHS參數(shù)且確保能工作。在確認CHS工作正常后可以增加LBA讀取特別是LBA28或LBA48來支持大容量硬盤和簡化操作。原書第三章很可能從CHS開始講解這是理解硬盤物理結(jié)構(gòu)的絕佳途徑。3.2 編程接口BIOS中斷 vs 端口直接控制BIOS中斷INT 13hBIOS提供了一組用于磁盤服務的中斷例程如INT 13h, AH02h用于讀扇區(qū)。使用簡單只需設置好寄存器參數(shù)即可調(diào)用。BIOS會自動處理底層細節(jié)兼容性好。端口直接控制PIO模式直接通過in和out指令與硬盤控制器IDE/AHCI的I/O端口通信。這種方式更底層、更高效且不依賴BIOS在進入保護模式后BIOS中斷不可用是唯一選擇。但編程復雜需要詳細了解硬盤控制器的寄存器定義和狀態(tài)機。設計決策在MBR階段優(yōu)先使用BIOS中斷。原因有三1) MBR運行在實模式下BIOS中斷完全可用且穩(wěn)定2) 代碼簡潔易于理解和調(diào)試3) 我們的目標是盡快加載第二級加載器而不是追求極致性能。將復雜的端口操作留給后續(xù)階段更為合適。因此本章完善MBR的核心就是學會正確調(diào)用INT 13h中斷來讀取硬盤扇區(qū)。3.3 加載目標地址與大小的規(guī)劃MBR自己被加載到0x7c00那么它應該把讀出來的數(shù)據(jù)放到哪里這需要精心規(guī)劃避免覆蓋自己或重要數(shù)據(jù)區(qū)。常見選擇0x90000這是一個在早期內(nèi)存布局中相對安全的高地址區(qū)域。0x7c00到0x7e00是MBR0x7e00往后到約0x9ffff是可用區(qū)域約609KB。將第二級加載器加載到0x90000576KB處留下了足夠的緩沖區(qū)也遠離了MBR和BIOS數(shù)據(jù)區(qū)。大小限制通過BIOS中斷INT 13h一次能讀取的扇區(qū)數(shù)是有限的通常受限于磁盤參數(shù)和BIOS實現(xiàn)。我們需要設計一個循環(huán)分批讀取直到將整個第二級加載器比如一個2KB的loader.bin完整載入內(nèi)存?;谝陨戏治霰菊碌膶崿F(xiàn)思路就清晰了編寫一個使用BIOS中斷INT 13h、以CHS模式或簡單LBA、循環(huán)讀取多個扇區(qū)到目標內(nèi)存如0x90000的匯編程序并跳轉(zhuǎn)到該內(nèi)存地址執(zhí)行。4. 關(guān)鍵代碼實現(xiàn)與逐行解析下面我們以一個典型的“完善MBR”代碼框架為例進行拆解。假設我們要從硬盤的第二個扇區(qū)LBA1注意扇區(qū)號通常從0開始計數(shù)開始讀取4個扇區(qū)即2KB的內(nèi)容到內(nèi)存0x90000處然后跳轉(zhuǎn)過去。; 文件名mbr.asm ; 描述完善的MBR具備加載第二級加載器的功能 SECTION MBR vstart0x7c00 ; 告訴編譯器代碼加載的基地址是0x7c00 mov ax, cs mov ds, ax mov es, ax mov ss, ax mov fs, ax mov sp, 0x7c00 ; 設置棧指針棧向下生長 ; 功能清屏 mov ax, 0x0600 mov bx, 0x0700 mov cx, 0 mov dx, 0x184f int 0x10 ; 功能設置光標位置到(0,0) mov ah, 0x02 mov bh, 0 mov dx, 0 int 0x10 ; 功能打印提示信息 mov si, msg_loading call print_string ; 核心功能從硬盤加載loader mov eax, 1 ; 起始扇區(qū)LBA號第2個扇區(qū) mov bx, 0x9000 ; 目標內(nèi)存段地址 (ES:BX - 0x9000:0x0000 0x90000) mov cx, 4 ; 待讀取的扇區(qū)數(shù) call read_disk_lba ; 調(diào)用讀取函數(shù) ; 加載完成跳轉(zhuǎn)到loader jmp 0x9000:0x0000 ; 遠跳轉(zhuǎn)跳轉(zhuǎn)到loader的入口點 ; 函數(shù)打印字符串以0結(jié)尾 print_string: mov ah, 0x0e .print_loop: lodsb ; 從[ds:si]加載一個字節(jié)到alsi自增 cmp al, 0 je .print_done int 0x10 jmp .print_loop .print_done: ret ; 函數(shù)使用LBA28模式讀取硬盤扇區(qū) ; 參數(shù)eaxLBA扇區(qū)號 es:bx目標內(nèi)存地址 cx讀取扇區(qū)數(shù) read_disk_lba: ; 1. 備份參數(shù) mov esi, eax mov di, cx ; 2. 設置讀取的扇區(qū)數(shù) mov dx, 0x1f2 mov al, cl out dx, al ; 3. 設置LBA地址28位 mov eax, esi ; LBA低8位 - 端口0x1f3 mov dx, 0x1f3 out dx, al ; LBA 8-15位 - 端口0x1f4 shr eax, 8 mov dx, 0x1f4 out dx, al ; LBA 16-23位 - 端口0x1f5 shr eax, 8 mov dx, 0x1f5 out dx, al ; LBA 24-27位 設備選擇與LBA模式 - 端口0x1f6 shr eax, 8 and al, 0x0f ; 低4位是LBA的最高4位 or al, 0xe0 ; 設置高4位1110表示LBA模式主設備 mov dx, 0x1f6 out dx, al ; 4. 發(fā)送讀命令0x20 mov dx, 0x1f7 mov al, 0x20 out dx, al ; 5. 等待硬盤準備就緒 .not_ready: in al, dx and al, 0x88 ; 檢查BSY位(bit7)和DRQ位(bit3) cmp al, 0x08 ; BSY0且DRQ1時表示數(shù)據(jù)就緒 jnz .not_ready ; 6. 從數(shù)據(jù)端口(0x1f0)讀取數(shù)據(jù) mov dx, 0x1f0 mov cx, di ; 恢復要讀取的扇區(qū)數(shù) .read_sector_loop: push cx mov cx, 256 ; 一個扇區(qū)512字節(jié)每次讀一個字(2字節(jié))共256次 .read_word: in ax, dx ; 從端口0x1f0讀一個字到ax mov [es:bx], ax ; 存儲到目標內(nèi)存 add bx, 2 loop .read_word pop cx loop .read_sector_loop ret ; 數(shù)據(jù)區(qū) msg_loading db Loading loader..., 0 ; 填充剩余空間并添加引導簽名 times 510-($-$$) db 0 db 0x55, 0xaa代碼關(guān)鍵點解析vstart0x7c00這是NASM編譯器的偽指令至關(guān)重要。它告訴編譯器所有標號的地址都基于0x7c00計算。例如msg_loading在文件中的偏移可能是0x00c0但在內(nèi)存中它的實際地址是0x7c00 0x00c0 0x7cc0。如果沒有這個跳轉(zhuǎn)和內(nèi)存訪問的地址會全部錯亂。棧的設置mov sp, 0x7c00將棧頂設在MBR的起始處棧向下向低地址生長。這為函數(shù)調(diào)用call提供了空間但要注意別讓棧數(shù)據(jù)覆蓋了MBR自身的代碼通常不會因為函數(shù)調(diào)用不深。read_disk_lba函數(shù)這是本章的靈魂。它使用了LBA28模式和**PIO編程輸入輸出**方式讀取硬盤。步驟分解步驟2-4向硬盤控制器的各個端口0x1f2-0x1f7寫入?yún)?shù)和命令。0x1f6端口的0xe0設置了LBA模式和選擇主盤。步驟5輪詢狀態(tài)端口0x1f7等待硬盤不忙BSY0且數(shù)據(jù)請求就緒DRQ1。這是一個阻塞等待循環(huán)。步驟6從數(shù)據(jù)端口0x1f0連續(xù)讀取數(shù)據(jù)。注意in ax, dx一次讀取16位一個字一個扇區(qū)512字節(jié)需要讀256次。外層循環(huán)控制讀取多個扇區(qū)。跳轉(zhuǎn)指令jmp 0x9000:0x0000。這是一個遠跳轉(zhuǎn)同時改變了CS和IP寄存器。CS被設為0x9000IP設為0處理器將從物理地址CS*16 IP 0x90000處開始執(zhí)行。這正是我們加載第二級加載器的地方。引導簽名最后的times 510-($-$$) db 0用于將MBR填充到510字節(jié)db 0x55, 0xaa是固定的引導簽名第511和512字節(jié)。BIOS依靠這個簽名來識別一個設備是否可引導。5. 從編寫到運行完整實操流程理解了代碼我們來看看如何將它變成可以運行的MBR并測試其加載功能。5.1 環(huán)境準備與工具鏈匯編編譯器推薦使用NASMNetwide Assembler。它輕量、高效是x86匯編的常用選擇。虛擬機使用Bochs或QEMU。Bochs調(diào)試功能極其強大適合深入學習QEMU啟動快速。這里以Bochs為例。磁盤映像工具可以使用dd命令Linux/macOS或WinImage等工具創(chuàng)建空白磁盤映像。5.2 編譯與寫入MBR編寫匯編代碼將上面的代碼保存為mbr.asm。編譯打開終端執(zhí)行nasm -f bin mbr.asm -o mbr.bin。-f bin指定輸出為純二進制格式這正是MBR需要的。創(chuàng)建磁盤映像dd if/dev/zero ofboot.img bs512 count2048。這創(chuàng)建一個1MB512*2048的空白映像。寫入MBRdd ifmbr.bin ofboot.img bs512 count1 convnotrunc。將編譯好的mbr.bin寫入映像的第一個扇區(qū)convnotrunc確保不截斷原文件。5.3 創(chuàng)建并放置第二級加載器Loader為了測試MBR的加載功能我們需要一個簡單的“Loader”程序。創(chuàng)建一個loader.asmSECTION LOADER vstart0x90000 mov si, msg_loader call print jmp $ ; 無限循環(huán) print: mov ah, 0x0e .print_loop: lodsb cmp al, 0 je .done int 0x10 jmp .print_loop .done: ret msg_loader db Hello from Loader!, 0編譯它nasm -f bin loader.asm -o loader.bin。注意這里沒有vstart0x7c00因為我們希望它的代碼基于0x90000編址。 將loader.bin寫入磁盤映像的第二個扇區(qū)dd ifloader.bin ofboot.img bs512 seek1 convnotrunc。seek1表示跳過第一個扇區(qū)MBR從第二個開始寫。5.4 配置與運行Bochs創(chuàng)建Bochs配置文件bochsrcmegs: 32 romimage: file$BXSHARE/BIOS-bochs-latest vgaromimage: file$BXSHARE/VGABIOS-lgpl-latest ata0-master: typedisk, pathboot.img, modeflat boot: disk display_library: sdl2運行Bochsbochs -f bochsrc -q。在Bochs提示符下輸入c繼續(xù)執(zhí)行。如果一切正常你將先看到“Loading loader...”然后屏幕清空因為跳轉(zhuǎn)后CS:IP改變可能中斷向量表等未妥善處理導致顯示異?;蛘週oader自己清屏最后看到“Hello from Loader!”。使用Bochs的內(nèi)置調(diào)試器如b 0x7c00設置斷點可以單步跟蹤整個加載過程觀察寄存器和內(nèi)存的變化這是不可多得的學習體驗。6. 深度原理實模式下的硬盤訪問機制要真正駕馭這段代碼必須理解其背后的硬件原理。我們使用的是IDE硬盤的PIO模式。6.1 硬盤控制器端口映射在x86架構(gòu)中通過I/O端口與硬件通信。主IDE通道被映射到一組固定的端口號0x1f0-0x1f7等。每個端口都有特定功能0x1f0數(shù)據(jù)端口讀寫數(shù)據(jù)都必須經(jīng)過它。0x1f1錯誤寄存器讀或特性寄存器寫。0x1f2扇區(qū)數(shù)寄存器。指定要讀/寫的扇區(qū)數(shù)量。0x1f3-0x1f5LBA低、中、高字節(jié)寄存器。0x1f6設備/磁頭寄存器。低4位是LBA的最高4位第4位0-based選擇主從盤0主1從第6位設置LBA模式1為LBA。0x1f7命令/狀態(tài)寄存器。寫入時是命令端口如0x20讀0x30寫讀取時是狀態(tài)端口。6.2 LBA28尋址詳解我們代碼中使用的是LBA28模式即使用28位二進制數(shù)來尋址扇區(qū)最大支持2^28 * 512 bytes 128GB的硬盤。28位的LBA地址被拆分到四個8位端口LBA 0-7位 -0x1f3LBA 8-15位 -0x1f4LBA 16-23位 -0x1f5LBA 24-27位 -0x1f6的低4位 向0x1f6寫入0xe0二進制1110 0000的含義是高4位1110其中第7、5位固定為1第6位1表示啟用LBA模式第4位0表示選擇主設備Master。6.3 狀態(tài)寄存器與等待循環(huán)發(fā)送讀命令后硬盤需要時間尋道和準備數(shù)據(jù)。程序必須等待。通過反復讀取狀態(tài)寄存器0x1f7來檢查BSY (Bit 7)硬盤忙。為1時其他位無效。DRQ (Bit 3)數(shù)據(jù)請求就緒。為1時表示數(shù)據(jù)已準備好可以通過數(shù)據(jù)端口讀取。 我們的等待循環(huán)檢查(AL 0x88) 0x08即等待BSY為0且DRQ為1。這是一種簡單的輪詢方式。更健壯的代碼還應檢查錯誤位ERR, Bit 0和磁盤就緒位RDY, Bit 6。6.4 數(shù)據(jù)讀取與內(nèi)存寫入數(shù)據(jù)就緒后從數(shù)據(jù)端口0x1f0連續(xù)讀取。每次in ax, dx指令會從端口讀取16位2字節(jié)到AX寄存器然后自動遞增內(nèi)部地址指針指向下一個字。這就是為什么我們用一個256次的循環(huán)讀完一個扇區(qū)。讀取的數(shù)據(jù)通過mov [es:bx], ax寫入ES:BX指向的內(nèi)存。ES在調(diào)用前已被設置為0x9000BX初始為0每次寫入后BX加2從而線性填充從0x90000開始的內(nèi)存區(qū)域。7. 常見問題、調(diào)試技巧與避坑指南即使代碼看似正確第一次嘗試也常常失敗。以下是我在實踐和教學中總結(jié)的常見問題點。7.1 問題一編譯通過但Bochs啟動后黑屏或重啟可能原因1引導簽名錯誤或位置不對。務必確保0x55在第511字節(jié)偏移0x1FE0xAA在第512字節(jié)偏移0x1FF。使用hexdump -C mbr.bin | tail -n 5檢查二進制文件末尾??赡茉?代碼中有非法操作或未初始化段寄存器。確保在操作內(nèi)存如mov [bx], ax前正確設置了DS或ES寄存器。我們的代碼開頭就統(tǒng)一設置了DSESSSCS??赡茉?棧設置不當導致意外覆蓋代碼。雖然不常見但如果進行了深度遞歸或大量壓棧棧向下生長可能會覆蓋0x7c00以下的代碼。確保??臻g足夠。7.2 問題二能看到“Loading loader...”但之后黑屏或亂碼看不到Loader的信息可能原因1Loader沒有正確寫入磁盤映像的第二個扇區(qū)。使用dd命令時務必確認seek1參數(shù)正確且loader.bin大小不超過你預留的扇區(qū)數(shù)??梢杂胔exdump -C boot.img | less查看磁盤映像內(nèi)容確認在偏移0x200512字節(jié)之后是你的Loader代碼??赡茉?讀取扇區(qū)數(shù)錯誤或目標內(nèi)存地址錯誤。檢查調(diào)用read_disk_lba時傳入的參數(shù)eax起始LBA、cx扇區(qū)數(shù)、es:bx目標地址。確保Loader確實被讀到了0x90000??梢栽贐ochs調(diào)試器中在讀取函數(shù)返回后使用xp /1024b 0x90000命令查看該內(nèi)存區(qū)域的內(nèi)容是否與loader.bin的二進制內(nèi)容一致??赡茉?Loader自身的代碼或編址問題。Loader中使用了vstart0x90000這意味著其中的標號如msg_loader地址是基于0x90000計算的。如果Loader里用了錯誤的段寄存器或跳轉(zhuǎn)也會失敗。一個簡單的調(diào)試方法是讓Loader的第一條指令就是一條顯式輸出字符的指令如mov ah, 0x0e; mov al, L; int 0x10先確認跳轉(zhuǎn)成功。7.3 問題三Bochs報告磁盤讀錯誤例如“ATA0-0: read failed”可能原因LBA地址超出磁盤范圍或磁盤模式不對。我們創(chuàng)建的boot.img只有2048個扇區(qū)1MB。如果你嘗試讀取的LBA地址大于等于2048就會出錯。確保eax參數(shù)正確。另外雖然我們設置了LBA模式但極少數(shù)非常古老的虛擬硬件或配置可能不支持可以嘗試回退到CHS模式進行對比測試。7.4 高級調(diào)試技巧Bochs魔法斷點在Bochs配置文件中加入magic_break: enabled1然后在匯編代碼中插入xchg bx, bx指令。Bochs遇到這條指令會自動中斷進入調(diào)試器。這是實模式下的軟件斷點神器。內(nèi)存與寄存器檢查在調(diào)試器中多用x /nuf addr命令查看內(nèi)存如x /16xb 0x7c00看MBR開頭用r或info r查看寄存器狀態(tài)。特別注意CS:IP和ES:BX的值。單步執(zhí)行與反匯編使用s單步執(zhí)行n跳過函數(shù)調(diào)用。使用u /10對當前CS:IP進行反匯編確保代碼執(zhí)行流符合預期。7.5 避坑心得始終保持段寄存器清晰在實模式下任何內(nèi)存訪問都隱式或顯式地依賴于段寄存器。在寫函數(shù)時想清楚這個函數(shù)會修改哪些段寄存器ES?DS?調(diào)用者是否需要保存它們。一個良好的習慣是函數(shù)如果修改了ES或DS在返回前恢復它們。小心LOOP指令LOOP指令使用CX寄存器作為計數(shù)器。如果你的函數(shù)內(nèi)部也用了CX務必在循環(huán)開始前push cx循環(huán)結(jié)束后pop cx或者使用其他寄存器如DI作為循環(huán)計數(shù)器避免沖突。硬盤操作是慢速操作等待循環(huán)是必須的但最好加入超時機制。例如循環(huán)超過一定次數(shù)如10000次后仍未就緒則視為錯誤跳轉(zhuǎn)到錯誤處理流程如打印錯誤碼后掛起。從簡單到復雜驗證不要一次性寫完整套加載邏輯。先寫一個MBR只讀一個扇區(qū)到內(nèi)存然后把這個扇區(qū)的內(nèi)容可以是一段簡單的顯示代碼寫回屏幕驗證讀取過程本身是否正確。然后再增加循環(huán)讀取多個扇區(qū)和跳轉(zhuǎn)邏輯。完善MBR的過程是真正開始與計算機硬件對話的第一步。它打破了“魔法”的屏障讓你清晰地看到操作系統(tǒng)那宏偉的大廈最初就是從這512字節(jié)的泥土中一磚一瓦地構(gòu)建起來的。當你看到自己編寫的Loader成功被MBR拉起并執(zhí)行時那種對系統(tǒng)掌控感的理解是任何理論教材都無法給予的。這不僅僅是完成書中的一章練習更是為你后續(xù)理解更復雜的保護模式切換、內(nèi)存分頁、驅(qū)動加載打下了堅實的地基。