banner
ShuWa

ShuWa

是进亦忧,退亦忧。然则何时而乐耶?
twitter

設備管理

設備控制器#

為了屏蔽設備之間的差異,每個設備都有一個叫設備控制器(Device Control)的組件,比如硬碟有硬碟控制器、顯示器有視頻控制器等。

控制器是有三類寄存器,它們分別是狀態寄存器(Status Register)、命令寄存器(Command Register)以及數據寄存器(Data Register),如下圖:

image

這三個寄存器的作用:

  • 數據寄存器,CPU 向 I/O 設備寫入需要傳輸的數據,比如要打印的內容是「Hello」,CPU 就要先發送一個 H 字符給到對應的 I/O 設備。
  • 命令寄存器,CPU 發送一個命令,告訴 I/O 設備,要進行輸入 / 輸出操作,於是就會交給 I/O 設備去工作,任務完成後,會把狀態寄存器裡面的狀態標記為完成。
  • 狀態寄存器,目的是告訴 CPU ,現在已經在工作或工作已經完成,如果已經在工作狀態,CPU 再發送數據或者命令過來,都是沒有用的,直到前面的工作已經完成,狀態寄存標記成已完成,CPU 才能發送下一個字符和命令。

CPU 通過讀寫設備控制器中的寄存器控制設備,這可比 CPU 直接控制輸入輸出設備,要方便和標準很多。

另外,輸入輸出設備可分為兩大類:塊設備(Block Device)和字符設備(Character Device)。

  • 塊設備,把數據存儲在固定大小的塊中,每個塊有自己的地址,硬碟、USB 是常見的塊設備。
  • 字符設備,以字符為單位發送或接收一個字符流,字符設備是不可尋址的,也沒有任何尋道操作,鼠標是常見的字符設備。

塊設備通常傳輸的數據量會非常大,於是控制器設立了一個可讀寫的數據緩衝區

  • CPU 寫入數據到控制器的緩衝區時,當緩衝區的數據囤夠了一部分,才會發給設備。
  • CPU 從控制器的緩衝區讀取數據時,也需要緩衝區囤夠了一部分,才拷貝到內存。

這樣做是為了,減少對設備的頻繁操作。

那 CPU 是如何與設備的控制寄存器和數據緩衝區進行通信的?存在兩個方法:

  • 端口 I/O,每個控制寄存器被分配一個 I/O 端口,可以通過特殊的組合語言指令操作這些寄存器,比如 in/out 類似的指令。
  • 內存映射 I/O,將所有控制寄存器映射到內存空間中,這樣就可以像讀寫內存一樣讀寫數據緩衝區。

I/O 控制方式#

中斷的方式對於頻繁讀寫數據的磁盤,並不友好,這樣 CPU 容易經常被打斷,會佔用 CPU 大量的時間。對於這一類設備的問題的解決方法是使用 DMADirect Memory Access) 功能,它可以使得設備在 CPU 不參與的情況下,能夠自行完成把設備 I/O 數據放入到內存。那要實現 DMA 功能要有 「DMA 控制器」硬件的支持。

image

設備驅動程序#

雖然設備控制器屏蔽了設備的眾多細節,但每種設備的控制器的寄存器、緩衝區等使用模式都是不同的,所以為了屏蔽「設備控制器」的差異,引入了設備驅動程序

設備控制器不屬於操作系統範疇,它是屬於硬件,而設備驅動程序屬於操作系統的一部分,操作系統的內核代碼可以像本地調用代碼一樣使用設備驅動程序的接口,而設備驅動程序是面向設備控制器的代碼,它發出操控設備控制器的指令後,才可以操作設備控制器。

不同的設備控制器雖然功能不同,但是設備驅動程序會提供統一的接口給操作系統,這樣不同的設備驅動程序,就可以以相同的方式接入操作系統。

前面提到了不少關於中斷的事情,設備完成了事情,則會發送中斷來通知操作系統。那操作系統就需要有一個地方來處理這個中斷,這個地方也就是在設備驅動程序裡,它會及時響應控制器發來的中斷請求,並根據這個中斷的類型調用響應的中斷處理程序進行處理。

image

通用塊層#

對於塊設備,為了減少不同塊設備的差異帶來的影響,Linux 通過一個統一的通用塊層,來管理不同的塊設備。

通用塊層是處於文件系統和磁盤驅動中間的一個塊設備抽象層,它主要有兩個功能:

  • 第一個功能,向上為文件系統和應用程序,提供訪問塊設備的標準接口,向下把各種不同的磁盤設備抽象為統一的塊設備,並在內核層面,提供一個框架來管理這些設備的驅動程序;
  • 第二功能,通用層還會給文件系統和應用程序發來的 I/O 請求排隊,接著會對隊列重新排序、請求合併等方式,也就是 I/O 調度,主要目的是為了提高磁盤讀寫的效率。

Linux 內核支持 5 種 I/O 調度算法,分別是:

  • 沒有調度算法:不對文件系統和應用程序的 I/O 做任何處理,這種算法常用在虛擬機 I/O 中,此時磁盤 I/O 調度算法交由物理機系統負責。
  • 先入先出調度算法
  • 完全公平調度算法:為每個進程維護了一個 I/O 調度隊列,並按照時間片來均勻分布每個進程的 I/O 請求。
  • 優先級調度
  • 最終期限調度算法:分別為讀、寫請求創建了不同的 I/O 隊列,這樣可以提高機械磁盤的吞吐量,並確保達到最終期限的請求被優先處理,適用於在 I/O 壓力比較大的場景,比如數據庫等

存儲系統 I/O 軟件分層#

可以把 Linux 存儲系統的 I/O 由上到下可以分為三個層次,分別是文件系統層、通用塊層、設備層。他們整個的層次關係如下圖:

image

這三個層次的作用是:

  • 文件系統層,包括虛擬文件系統和其他文件系統的具體實現,它向上為應用程序統一提供了標準的文件訪問接口,向下會通過通用塊層來存儲和管理磁盤數據。
  • 通用塊層,包括塊設備的 I/O 隊列和 I/O 調度器,它會對文件系統的 I/O 請求進行排隊,再通過 I/O 調度器,選擇一個 I/O 發給下一層的設備層。
  • 設備層,包括硬件設備、設備控制器和驅動程序,負責最終物理設備的 I/O 操作。

有了文件系統接口之後,不但可以通過文件系統的命令行操作設備,也可以通過應用程序,調用 read、write 函數,就像讀寫文件一樣操作設備,所以說設備在 Linux 下,也只是一個特殊的文件。

但是,除了讀寫操作,還需要有檢查特定於設備的功能和屬性。於是,需要 ioctl 接口,它表示輸入輸出控制接口,是用於配置和修改特定設備屬性的通用接口。

另外,存儲系統的 I/O 是整個系統最慢的一個環節,所以 Linux 提供了不少緩存機制來提高 I/O 的效率。

  • 為了提高文件訪問的效率,會使用頁緩存、索引節點緩存、目錄項緩存等多種緩存機制,目的是為了減少對塊設備的直接調用。
  • 為了提高塊設備的訪問效率, 會使用緩衝區,來緩存塊設備的數據。

鍵盤敲入字母時,期間發生了什麼?#

image
CPU 裡面的內存接口,直接和系統匯流排通信,然後系統匯流排再接入一個 I/O 橋接器,這個 I/O 橋接器,另一邊接入了內存匯流排,使得 CPU 和內存通信。再另一邊,又接入了一個 I/O 匯流排,用來連接 I/O 設備,比如鍵盤、顯示器等。

那當用戶輸入了鍵盤字符,鍵盤控制器就會產生掃描碼數據,並將其緩衝在鍵盤控制器的寄存器中,緊接著鍵盤控制器通過匯流排給 CPU 發送中斷請求

CPU 收到中斷請求後,操作系統會保存被中斷進程的 CPU 上下文,然後調用鍵盤的中斷處理程序

鍵盤的中斷處理程序是在鍵盤驅動程序初始化時註冊的,那鍵盤中斷處理函數的功能就是從鍵盤控制器的寄存器的緩衝區讀取掃描碼,再根據掃描碼找到用戶在鍵盤輸入的字符,如果輸入的字符是顯示字符,那就會把掃描碼翻譯成對應顯示字符的 ASCII 碼,比如用戶在鍵盤輸入的是字母 A,是顯示字符,於是就會把掃描碼翻譯成 A 字符的 ASCII 碼。

得到了顯示字符的 ASCII 碼後,就會把 ASCII 碼放到「讀緩衝區隊列」,接下來就是要把顯示字符顯示屏幕了,顯示設備的驅動程序會定時從「讀緩衝區隊列」讀取數據放到「寫緩衝區隊列」,最後把「寫緩衝區隊列」的數據一個一個寫入到顯示設備的控制器的寄存器中的數據緩衝區,最後將這些數據顯示在屏幕裡。

顯示出結果後,恢復被中斷進程的上下文

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