一篇帶你了解Linux的輸入輸出
總線
Intel采用雙獨立總線(英語:Dual Independent Bus,DIB),使用外部的前端總線到主系統存儲器,和內部的后端總線于一個或多個中央處理器、CPU緩存間。CPU 里面的內存接口,直接和系統總線通信,然后系統總線再接入一個 I/O 橋接器(I/O Bridge)。這個 I/O 橋接器,一邊接入了我們的內存總線,使得我們的 CPU 和內存通信;另一邊呢,又接入了一個 I/O 總線,用來連接 I/O 設備。

在物理層面,其實我們完全可以把總線看作一組“電線”。一般有五類線路。
- 數據總線(Data Bus):在CPU與RAM之間來回傳送需要處理或是需要儲存的數據。
- 地址總線(Address Bus):用來指定在RAM(Random Access Memory)之中儲存的數據的地址。
- 控制總線(Control Bus):將微處理器控制單元(Control Unit)的信號,傳送到周邊設備,一般常見的為USB Bus和1394 Bus。
- 擴展總線(Expansion Bus):可連接擴展槽和電腦。
- 局部總線(Local Bus):取代更高速數據傳輸的擴展
I/O設備
輸入輸出設備,并不只是一個設備。大部分的輸入輸出設備,都有兩個組成部分。第一個是它的接口(Interface),第二個才是實際的 I/O 設備(Actual I/O Device)。我們的硬件設備并不是直接接入到總線上和 CPU 通信的,而是通過接口,用接口連接到總線上,再通過總線和 CPU 通信。比如可以把硬盤分為 IDE(Integrated Drive Electronics)、SCSI(Small Computer System Interface) 、SAS(Serial Attached SCSI) 、SATA(Serial ATA) 、FC(Fibre Channel)都是計算機主板上內置的各個接口。硬件只需要提供對應接口的設備驅動程序。
設備控制器
接口本身就是設備控制器。CPU 其實不是和實際的硬件設備打交道,而是和設備控制器打交道。設備控制器除了要將設備與計算機連接外,還有很多重要任務。
- 隨時監控設備所處的狀態,實現對設備的控制與操作。
- 設備控制器中都有三類寄存器:分別是狀態寄存器(Status Register)、 命令寄存器(Command Register)以及數據寄存器(Data Register)。每個控制寄存器被分配一個 I/O 端口,我們可以通過特殊的匯編指令(例如 in/out 類似的指令)操作這些寄存器。狀態寄存器,可以通過檢測狀態標志位,來確定輸入或者輸出操作是否完成。
- 有些設備還有數據緩沖區。如打印機等。可內存映射 I/O,可以分配一段內存空間給它,就像讀寫內存一樣讀寫數據緩沖區。
- 設備控制器還監管對由I/O設備傳送來的數據進行差錯檢測。若發現傳送中出現了錯誤,通常是將差錯檢測碼置位,并向CPU報告,于是CPU將本次傳送來的數據作廢,并重新進行一次傳送。這樣便可保證數據輸入的正確性。
設備驅動程序
用于實現設備對具體設備的管理與操作。要讓設備工作,必選訪問設備控制器中的各種寄存器,這部分通過編寫特定的程序代碼來實現程序,就是“設備驅動程序”。主要有以下功能:
- 對設備進行初始化
- 使設備投入運行和退出服務
- 從設備接收數據并將它們送回內核
- 將數據從內核送到設備
- 檢測和處理設備出現的錯誤
DMA控制器
有的設備需要讀取或者寫入大量數據。如果所有過程都讓 CPU 協調的話,就需要占用 CPU 大量的時間,比方說,磁盤就是這樣的。這種類型的設備需要支持 DMA 功能,也就是說,允許設備在 CPU 不參與的情況下,能夠自行完成對內存的讀寫。實現 DMA 機制需要有個 DMA 控制器幫你的 CPU 來做協調,就像下面這個圖中顯示的一樣。
- CPU 只需要對 DMA 控制器下指令,說它想讀取多少數據,放在內存的某個地方就可以了。
- 接下來 DMA 控制器會發指令給磁盤控制器,請求數據傳送到內存。磁盤驅動器讀取磁盤上的數據到磁盤控制器的內核緩沖區,磁盤控制器進行差錯校驗,保證沒有發生讀錯誤發生。磁盤控制器的寄存器,CPU與DMA都可以修改。
- 磁盤控制器從其內部緩沖區中讀取數據的時候知道這個數據該寫到什么地方。然后通過內存總線將數據寫到內存。
- 當寫操作完成時,磁盤控制器在總線上發出一個確認成功的信號到DMA控制器。
- DMA 控制器發中斷通知 CPU 指令完成,CPU 就可以直接用內存里面現成的數據了。
中斷控制器
硬件的中斷控制器,當設備完成任務后觸發中斷到中斷控制器,中斷控制器就通知 CPU,一個中斷產生了,CPU 需要停下當前手里的事情來處理中斷。一般的流程是,一個設備驅動程序初始化的時候,要先注冊一個該設備的中斷處理函數。中斷的時候,觸發的函數是 do_IRQ。這個函數是中斷處理的統一入口。在這個函數里面,我們可以找到設備驅動程序注冊的中斷處理函數 Handler,然后執行它進行中斷處理。
磁盤驅動的實現
在Linux中,設備驅動程序是一組相關函數的集合。它包含設備服務子程序和中斷處理程序。設備服務子程序包含了所有與設備相關的代碼,每個設備服務子程序只處理一種設備或者緊密相關的設備。其功能就是從與設備無關的軟件中接受抽象的命令并執行之。當執行一條請求時,具體操作是根據設備控制器對驅動程序提供的接口(指的是控制器中的各種寄存器),并利用中斷機制去調用中斷服務子程序配合設備來完成這個請求。設備驅動程序利用結構 file_operations 與文件系統聯系起來,即設備的各種操作的入口函數存在file_operation中。對于特定的設備來說有一些操作是不必要的,其入口置為NULL。
Linux 內核中雖存在許多不同的設備驅動程序但它們具有一些共同的特性:
- 驅動程序屬于內核代碼,設備驅動程序是內核的一部分,它象內核中其它代碼一樣運行在內核模式,驅動程序如果出錯將會使操作系統受到嚴重破壞,甚至能使系統崩潰并導致文件系統的破壞和數據丟失。
- 為內核提供統一的接口,設備驅動程序必須為 Linux 內核或其它子系統提供一個標準的接口。例如終端驅動程序為Linux 內核提供了一個文件 I/O 接口。
- 驅動程序的執行是屬于內核機制并且使用內核服務 。設備驅動可以使用標準的內核服務如內存分配、中斷發送和等待隊列等等。
- 動態可加載,多數 Linux 設備驅動程序可以在內核模塊發出加載請求時加載,而不再使用時將其卸載。這樣內核能有效地利用系統資源。
- 可配置,Linux 設備驅動程序可以連接到內核中。當內核被編譯時,被連入內核的設備驅動程序是可配置的。
這樣Linux的輸入輸出就很明朗了
輸入輸出設備的設備廠商很多。因為設備廠商復雜多變,設備廠商也同樣復雜多變,需要層層屏蔽差異化的部分,給上層提供標準化的部分,最終到用戶態,給用戶提供了基于文件系統的統一的接口。