本文轉載於http://blog.codepiano.com/2017/03/01/read-file-from-disk
CPU 和硬盤的關系是不太好描述,CPU 本質上只是用來執行指令,具體的讀取文件的操作是操作系統來做的,從操作系統的角度來說可能要方便一些。像其他答案說的,你的這些疑問應該去看操作系統和計算機組成原理相關的教材,形成一個整體上認識,而不應該片面的了解某一個方面。
我下面簡單敘述一下操作系統在從硬盤讀文件的流程。
為簡單起見,假設場景是一個x86體系的32位Linux操作系統中運行的進程 P 需要讀取文件 /home/user/test.txt,文件系統使用 ext3。
- Linux 系統提供了和 IO 相關的系統調用,進程 P 如果要讀取文件,首先需要發起系統調用(System Call) open,傳入文件路徑”/home/user/test.txt” 和相關參數,來打開文件,執行系統調用以后操作系統會從用戶態轉換到內核態,部分 CPU 提供了”trap”或者”syscall” 指令來完成狀態切換,切換到內核態以后,操作系統調用相應的處理器(handler) 開始處理讀取文件的請求。
- 系統調用 open 並不會直接讀取文件內容返回給進程,而是先進行權限方面的檢查,如果進程可以訪問這個文件,就根據文件路徑去查找文件對應的 inode 編號。這部分屬於文件系統的內容,在這里簡單說一下,每一個非軟鏈接的文件或者目錄都具有一個惟一的 inode 編號,對應 inode table 中的一個 inode 數據結構,inode 中包含文件大小,修改時間之類的元信息,也包括一個樹形的結構,這個樹形結構里面索引了文件內容存儲在硬盤的哪些扇區(sector)里。每個目錄也有一個對應的磁盤文件來存儲該目錄中直接包含的子文件或子目錄的名字和 inode 編號。比如查找”/home/user/test.txt”,需要按照路徑逐級查找,首先根目錄 / 的 inode 編號是約定的,為2,操作系統通過 inode 編號2這個信息去 inode table 中找到根目錄對應的 inode 信息,根據 inode 信息讀取磁盤扇區獲取文件內容(這里已經需要訪問磁盤了,下面再詳細說訪問過程),里面存儲的內容比較復雜,為了提升檢索效率可能會使用 B+樹之類的進行了索引,這里為描述方便,簡化一下,比如 根目錄下有3個目錄home、etc、bin,1個文件 eg.txt,那么根目錄對應的文件內容大概類似於
home 3 etc 4 bin 5 eg.txt 5
后面的數字就是目錄或者文件對應的 inode 編號,通過檢索可知,我們需要的找到 home 目錄的inode 編號為3,重復這個過程,直到定位到 /home/user/test.txt 的 inode 編號。文件系統所有的 inode 信息也是存儲在硬盤上的,也就是說硬盤中不僅有文件內容,還有文件系統的數據,這可以解釋你的第二、三個問題,為什么換了硬盤仍然能夠開機和識別文件,開機是存儲在主板上的 BIOS 引導的,操作系統啟動以后從硬盤讀取文件系統的數據就可以獲得整個磁盤的文件信息,CPU 只是執行操作系統的指令。
- 在獲取 inode 以后,操作系統生成了一個文件描述符(file descriptor),存儲在進程 P 自己的 file descriptors 數據結構中,通過文件描述符可以索引到文件的打開方式(只讀、讀寫等)還有 要打開文件的 inode。然后操作系統將文件描述符返回給進程 P,至此系統調用 open 完成。
- 進程 P 獲取到文件 /home/user/test.txt 的描述符以后,還需要再發起系統調用 read,傳入文件描述符來讀取文件內容,同樣讀取操作需要切換到內核態由內核代為完成。切換到內核態以后,操作系統通過文件描述符找到對應的 inode ,通過 inode 來確定文件存儲在磁盤哪些扇區中,然后向磁盤發送指令來讀取這些扇區,把內容讀取到內核的地址空間里面。一般來說操作系統會通過 memory mapped IO 技術把鍵盤、磁盤等硬件上的寄存器連接到 IO 總線,再通過 IO 控制器連接到內存總線,這樣硬件上的寄存器也被映射到了一段內存地址上,CPU 可以直接通過讀寫內存的指令來讀寫硬件寄存器中的數據。同時還會通過 DMA 技術來讓硬件不通過 CPU,直接讀寫內存的內容,這樣磁盤在傳輸文件的同時 CPU 可以去執行其他線程。
- 磁盤的 IO 操作完成后,磁盤會觸發一個中斷(interrupt),CPU 會暫時中止當前線程的執行,保存相關的寄存器信息后,調用對應的中斷處理器(interrupt handler),把讀取到的內容從內核地址空間拷貝到進程 P 的地址空間里面,然后將進程 P的狀態設置為 runnable, 進程 P 排隊等待自己的 CPU 時間片,被調度器調度以后可以繼續執行。