完了,這個硬件成精了,它竟然繞過了 CPU...

我們之前瞭解過了 Linux 的進程和線程、Linux 內存管理,那麼下面我們就來認識一下 Linux 中的 I/O 管理。

Linux 系統和其他 UNIX 系統一樣,IO 管理比較直接和簡潔。所有 IO 設備都被當作文件,通過在系統內部使用相同的 read 和 write 一樣進行讀寫。

Linux IO 基本概念

Linux 中也有磁盤、打印機、網絡等 I/O 設備,Linux 把這些設備當作一種 特殊文件 整合到文件系統中,一般通常位於 /dev 目錄下。可以使用與普通文件相同的方式來對待這些特殊文件。

特殊文件一般分爲兩種:

塊特殊文件是一個能存儲固定大小塊信息的設備,它支持以固定大小的塊,扇區或羣集讀取和(可選)寫入數據。每個塊都有自己的物理地址。通常塊的大小在 512 - 65536 之間。所有傳輸的信息都會以連續的塊爲單位。塊設備的基本特徵是每個塊都較爲對立,能夠獨立的進行讀寫。常見的塊設備有 硬盤、藍光光盤、USB 盤與字符設備相比,塊設備通常需要較少的引腳。

塊特殊文件的缺點基於給定固態存儲器的塊設備比基於相同類型的存儲器的字節尋址要慢一些,因爲必須在塊的開頭開始讀取或寫入。所以,要讀取該塊的任何部分,必須尋找到該塊的開始,讀取整個塊,如果不使用該塊,則將其丟棄。要寫入塊的一部分,必須尋找到塊的開始,將整個塊讀入內存,修改數據,再次尋找到塊的開頭處,然後將整個塊寫回設備。

另一類 I/O 設備是字符特殊文件。字符設備以字符爲單位發送或接收一個字符流,而不考慮任何塊結構。字符設備是不可尋址的,也沒有任何尋道操作。常見的字符設備有 打印機、網絡設備、鼠標、以及大多數與磁盤不同的設備

每個設備特殊文件都會和 設備驅動 相關聯。每個驅動程序都通過一個 主設備號 來標識。如果一個驅動支持多個設備的話,此時會在主設備的後面新加一個 次設備號 來標識。主設備號和次設備號共同確定了唯一的驅動設備。

我們知道,在計算機系統中,CPU 並不直接和設備打交道,它們中間有一個叫作 設備控制器(Device Control Unit)的組件,例如硬盤有磁盤控制器、USB 有 USB 控制器、顯示器有視頻控制器等。這些控制器就像代理商一樣,它們知道如何應對硬盤、鼠標、鍵盤、顯示器的行爲。

絕大多數字符特殊文件都不能隨機訪問,因爲他們需要使用和塊特殊文件不同的方式來控制。比如,你在鍵盤上輸入了一些字符,但是你發現輸錯了一個,這時有一些人喜歡使用 backspace 來刪除,有人喜歡用 del 來刪除。爲了中斷正在運行的設備,一些系統使用 ctrl-u 來結束,但是現在一般使用 ctrl-c 來結束。

網絡

I/O 的另外一個概念是網絡, 也是由 UNIX 引入,網絡中一個很關鍵的概念就是 套接字(socket)。套接字允許用戶連接到網絡,正如郵筒允許用戶連接到郵政系統,套接字的示意圖如下

套接字的位置如上圖所示,套接字可以動態創建和銷燬。成功創建一個套接字後,系統會返回一個文件描述符(file descriptor),在後面的創建鏈接、讀數據、寫數據、解除連接時都需要使用到這個文件描述符。每個套接字都支持一種特定類型的網絡類型,在創建時指定。一般最常用的幾種

  • 可靠的面向連接的字節流
  • 可靠的面向連接的數據包
  • 不可靠的數據包傳輸

可靠的面向連接的字節流會使用管道 在兩臺機器之間建立連接。能夠保證字節從一臺機器按照順序到達另一臺機器,系統能夠保證所有字節都能到達。

除了數據包之間的分界之外,第二種類型和第一種類型是類似的。如果發送了 3 次寫操作,那麼使用第一種方式的接受者會直接接收到所有字節;第二種方式的接受者會分 3 次接受所有字節。除此之外,用戶還可以使用第三種即不可靠的數據包來傳輸,使用這種傳輸方式的優點在於高性能,有的時候它比可靠性更加重要,比如在流媒體中,性能就尤其重要。

以上涉及兩種形式的傳輸協議,即 TCPUDP,TCP 是 傳輸控制協議,它能夠傳輸可靠的字節流。UDP用戶數據報協議,它只能夠傳輸不可靠的字節流。它們都屬於 TCP/IP 協議簇中的協議,下面是網絡協議分層

可以看到,TCP 、UDP 都位於網絡層上,可見它們都把 IP 協議 即 互聯網協議 作爲基礎。

一旦套接字在源計算機和目的計算機建立成功,那麼兩個計算機之間就可以建立一個鏈接。通信一方在本地套接字上使用 listen 系統調用,它就會創建一個緩衝區,然後阻塞直到數據到來。另一方使用 connect 系統調用,如果另一方接受 connect 系統調用後,則系統會在兩個套接字之間建立連接。

socket 連接建立成功後就像是一個管道,一個進程可以使用本地套接字的文件描述符從中讀寫數據,當連接不再需要的時候使用 close 系統調用來關閉。

Linux I/O 系統調用

Linux 系統中的每個 I/O 設備都有一個特殊文件(special file)與之關聯,什麼是特殊文件呢?

在操作系統中,特殊文件是一種在文件系統中與硬件設備相關聯的文件。特殊文件也被稱爲 設備文件(device file)。特殊文件的目的是將設備作爲文件系統中的文件進行公開。特殊文件爲硬件設備提供了藉口,用於文件 I/O 的工具可以進行訪問。因爲設備有兩種類型,同樣特殊文件也有兩種,即字符特殊文件和塊特殊文件

對於大部分 I/O 操作來說,只用合適的文件就可以完成,並不需要特殊的系統調用。然後,有時需要一些設備專用的處理。在 POSIX 之前,大多數 UNIX 系統會有一個叫做 ioctl 的系統調用,它用於執行大量的系統調用。隨着時間的發展,POSIX 對其進行了整理,把 ioctl 的功能劃分爲面向終端設備的獨立功能調用,現在已經變成獨立的系統調用了。

下面是幾個管理終端的系統調用

Linux IO 實現

Linux 中的 IO 是通過一系列設備驅動實現的,每個設備類型對應一個設備驅動。設備驅動爲操作系統和硬件分別預留接口,通過設備驅動來屏蔽操作系統和硬件的差異。

當用戶訪問一個特殊的文件時,由文件系統提供此特殊文件的主設備號和次設備號,並判斷它是一個塊特殊文件還是字符特殊文件。主設備號用於標識字符設備還是塊設備,次設備號用於參數傳遞。

每個驅動程序 都有兩部分:這兩部分都是屬於 Linux 內核,也都運行在內核態下。上半部分運行在調用者上下文並且與 Linux 其他部分交互。下半部分運行在內核上下文並且與設備進行交互。驅動程序可以調用內存分配、定時器管理、DMA 控制等內核過程。可被調用的內核功能都位於 驅動程序 - 內核接口 的文檔中。

I/O 實現指的就是對字符設備和塊設備的實現

塊設備實現

系統中處理塊特殊文件 I/O 部分的目標是爲了使傳輸次數儘可能的小。爲了實現這個目標,Linux 系統在磁盤驅動程序和文件系統之間設置了一個 高速緩存(cache) ,如下圖所示

在 Linux 內核 2.2 之前,Linux 系統維護着兩個緩存:頁面緩存(page cache)緩衝區緩存(buffer cache),因此,存儲在一個磁盤塊中的文件可能會在兩個緩存中。2.2 版本以後 Linux 內核只有一個統一的緩存一個 通用數據塊層(generic block layer) 把這些融合在一起,實現了磁盤、數據塊、緩衝區和數據頁之間必要的轉換。那麼什麼是通用數據塊層?

通用數據塊層是一個內核的組成部分,用於處理對系統中所有塊設備的請求。通用數據塊主要有以下幾個功能

將數據緩衝區放在內存高位處,當 CPU 訪問數據時,頁面纔會映射到內核線性地址中,並且此後取消映射

實現 零拷貝機制,磁盤數據可以直接放入用戶模式的地址空間,而無需先複製到內核內存中

管理磁盤卷,會把不同塊設備上的多個磁盤分區視爲一個分區。

利用最新的磁盤控制器的高級功能,例如 DMA 等。

cache 是提升性能的利器,不管以什麼樣的目的需要一個數據塊,都會先從 cache 中查找,如果找到直接返回,避免一次磁盤訪問,能夠極大的提升系統性能。

如果頁面 cache 中沒有這個塊,操作系統就會把頁面從磁盤中調入內存,然後讀入 cache 進行緩存。

cache 除了支持讀操作外,也支持寫操作,一個程序要寫回一個塊,首先把它寫到 cache 中,而不是直接寫入到磁盤中,等到磁盤中緩存達到一定數量值時再被寫入到 cache 中。

Linux 系統中使用 IO 調度器 來保證減少磁頭的反覆移動從而減少損失。I/O 調度器的作用是對塊設備的讀寫操作進行排序,對讀寫請求進行合併。Linux 有許多調度器的變體,從而滿足不同的工作需要。最基本的 Linux 調度器是基於傳統的 Linux 電梯調度器(Linux elevator scheduler)。Linux 電梯調度器的主要工作流程就是按照磁盤扇區的地址排序並存儲在一個雙向鏈表 中。新的請求將會以鏈表的形式插入。這種方法可以有效的防止磁頭重複移動。因爲電梯調度器會容易產生飢餓現象。因此,Linux 在原基礎上進行了修改,維護了兩個鏈表,在 最後日期(deadline) 內維護了排序後的讀寫操作。默認的讀操作耗時 0.5s,默認寫操作耗時 5s。如果在最後期限內等待時間最長的鏈表沒有獲得服務,那麼它將優先獲得服務。

字符設備實現

和字符設備的交互是比較簡單的。由於字符設備會產生並使用字符流、字節數據,因此對隨機訪問的支持意義不大。一個例外是使用 行規則(line disciplines)。一個行規可以和終端設備相關聯,使用 tty_struct 結構來表示,它表示與終端設備交換數據的解釋器,當然這也屬於內核的一部分。例如:行規可以對行進行編輯,映射回車爲換行等一系列其他操作。

什麼是行規則?

行規是某些類 UNIX 系統中的一層,終端子系統通常由三層組成:上層提供字符設備接口,下層硬件驅動程序與硬件或僞終端進行交互,中層規則用於實現終端設備共有的行爲。

網絡設備實現

網絡設備的交互是不一樣的,雖然 網絡設備(network devices) 也會產生字符流,因爲它們的異步(asynchronous) 特性是他們不易與其他字符設備在同一接口下集成。網絡設備驅動程序會產生很多數據包,經由網絡協議到達用戶應用程序中。

Linux 中的模塊

UNIX 設備驅動程序是被靜態加載到內核中的。因此,只要系統啓動後,設備驅動程序都會被加載到內存中。隨着個人電腦 Linux 的出現,這種靜態鏈接完成後會使用一段時間的模式被打破。相對於小型機上的 I/O 設備,PC 上可用的 I/O 設備有了數量級的增長。絕大多數用戶沒有能力去添加一個新的應用程序、更新設備驅動、重新連接內核,然後進行安裝。

Linux 爲了解決這個問題,引入了 可加載(loadable module) 機制。可加載是在系統運行時添加到內核中的代碼塊。

當一個模塊被加載到內核時,會發生下面幾件事情:第一,在加載的過程中,模塊會被動態的重新部署。第二,系統會檢查程序程序所需的資源是否可用。如果可用,則把這些資源標記爲正在使用。第三步,設置所需的中斷向量。第四,更新驅動轉換表使其能夠處理新的主設備類型。最後再來運行設備驅動程序。

在完成上述工作後,驅動程序就會安裝完成,其他現代 UNIX 系統也支持可加載機制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章