Java I/O底層是如何工作的?

ImportNew - liken發表於2014-12-19

本博文主要討論I/O在底層是如何工作的。本文服務的讀者,迫切希望瞭解Java I/O操作是在機器層面如何進行對映,以及應用執行時硬體都做了什麼。假定你熟悉基本的I/O操作,比如通過Java I/O API讀寫檔案。這些內容不在本文的討論範圍。

目錄

快取處理和核心vs使用者空間

緩衝與緩衝的處理方式,是所有I/O操作的基礎。術語“輸入、輸出”只對資料移入和移出快取有意義。任何時候都要把它記在心中。通常,程式執行作業系統的I/O請求包括資料從緩衝區排出(寫操作)和資料填充緩衝區(讀操作)。這就是I/O的整體概念。在作業系統內部執行這些傳輸操作的機制可以非常複雜,但從概念上講非常簡單。我們將在文中用一小部分來討論它。

 

 

 

上圖顯示了一個簡化的“邏輯”圖,它表示塊資料如何從外部源,例如一個磁碟,移動到程式的儲存區域(例如RAM)中。首先,程式要求其緩衝通過read()系統呼叫填滿。這個系統呼叫導致核心向磁碟控 制硬體發出一條命令要從磁碟獲取資料。磁碟控制器通過DMA直接將資料寫入核心的記憶體緩衝區,不需要主CPU進一步幫助。當請求read()操作時,一旦磁碟控制器完成了快取的填 寫,核心從核心空間的臨時快取拷貝資料到程式指定的快取中。

有一點需要注意,在核心試圖快取及預取資料時,核心空間中程式請求的資料可能已經就緒了。如果這樣,程式請求的資料會被拷貝出來。如果資料不可用,則程式被掛起。核心將把資料讀入記憶體。

虛擬記憶體

你可能已經多次聽說過虛擬記憶體了。讓我再介紹一下。

所有現代作業系統都使用虛擬記憶體。虛擬記憶體意味著人工或者虛擬地址代替物理(硬體RAM)記憶體地址。虛擬地址有兩個重要優勢:

  1. 多個虛擬地址可以對映到相同的實體地址。
  2. 一個虛擬地址空間可以大於實際可用硬體記憶體。

在上面介紹中,從核心空間拷貝到終端使用者快取看起來增加了額外的工作。為什麼不告訴磁碟控制器直接傳送資料到使用者空間的快取呢?好吧,這是由虛擬記憶體實現的。用到了上面的優勢1。

通過將核心空間地址對映到相同的實體地址作為一個使用者空間的虛擬地址,DMA硬體(只能方位實體記憶體地址)可以填充快取。這個快取同時對核心和使用者空間程式可見。

 

 

 

這就消除了核心和使用者空間之間的拷貝,但是需要核心和使用者緩衝區使用相同的頁面對齊方式。緩衝區必須使用的塊大小的倍數磁碟控制器(通常是512位元組的磁碟扇區)。作業系統將其記憶體地址空間劃分為頁面,這是固定大小的位元組組。這些記憶體頁總是磁碟塊大小的倍數和通常為2倍(簡化定址)。典型的記憶體頁面大小是1024、2048和4096位元組。虛擬和實體記憶體頁面大小總是相同的。

記憶體分頁

為了支援虛擬記憶體的第2個優勢(擁有大於物理內 存的可定址空間)需要進行虛擬記憶體分頁(通常稱為頁交換)。這種機制憑藉虛擬記憶體空間的頁可以持久儲存在外部磁碟儲存,從而為其他虛擬頁放入實體記憶體提供了空間。本質上講,實體記憶體擔當了分頁區域的快取。分頁區是磁碟上的空間,記憶體頁的內容被強迫交換出實體記憶體時會儲存到這裡。

調整記憶體頁面大小為磁碟塊大小的倍數,讓核心可以直接傳送指令到磁碟控制器硬體,將記憶體頁寫到磁碟或者在需要時重新載入。事實證明,所有的磁碟I/O操作都是在頁面級別上完成的。這是資料在現代分頁作業系統上在磁碟與實體記憶體之間移動的唯一方式。

現代CPU包含一個名為記憶體管理單元(MMU)的子系統。這 個裝置邏輯上位於CPU與實體記憶體之間。它包含從虛擬地址向實體記憶體地址轉化的對映資訊。當CPU引用一個記憶體位置時,MMU決定哪些頁需要駐留(通常通過移位或遮蔽地址的某些位)以及轉化虛擬頁號到物理頁號(由硬體實現,速度奇快)。

面向檔案I/O

檔案I/O總是發生在檔案系統的上下文切換中。檔案系統跟磁碟是完全不同的事物。磁碟按段儲存資料,每段512位元組。它是硬體裝置,對儲存的檔案語義一無所知。它們只是提供了一定數量的可以儲存資料的插槽。從這方面來說,一個磁碟的段與 記憶體分頁類似。它們都有統一的大小並且是個可定址的大陣列。

另一方面,檔案系統是更高層抽象。檔案系統是安排和翻譯儲存磁碟(或其它可隨機訪問,面向塊的裝置)資料的一種特殊方法。你寫的程式碼幾乎總是與檔案系統互動,而不與磁碟直接互動。檔案系統定義了檔名、路徑、檔案、檔案屬性等抽象。

一個檔案系統組織(在硬碟中)了一系列均勻大小的資料塊。有些塊儲存元資訊,如空閒塊的對映、目錄、索引等。其它塊包含實際的檔案資料。單個檔案的元資訊描述哪些塊包含檔案資料、資料結束位置、最後更新時間等。當使用者程式傳送請求來讀取檔案資料時,檔案系統實現準確定位資料在磁碟上的位置。然後採取行動將這些磁碟扇區放入記憶體中。

檔案系統也有頁的概念,它的大小可能與一個基本記憶體頁面大小相同或者是它的倍數。典型的檔案系統頁面大小範圍從2048到8192位元組,並且總是一個基本記憶體頁面大小的倍數。

分頁檔案系統執行I/O可以歸結為以下邏輯步驟:

  1. 確定請求跨越了哪些檔案系統分頁(磁碟段的集合)。磁碟上的檔案內容及後設資料可能分佈在多個檔案系統頁面上,這些頁面可能是不連續的。
  2. 分配足夠多的核心空間記憶體頁面來儲存相同的檔案系統頁面。
  3. 建立這些記憶體分頁與磁碟上檔案系統分頁的對映。
  4. 對每一個記憶體分頁產生分頁錯誤。
  5. 虛擬記憶體系統陷入分頁錯誤並且排程pagins(頁面調入),通過從磁碟讀取內容來驗證這些頁面。
  6. 一旦pageins完成,檔案系統分解原始資料來提取請求的檔案內容或屬性資訊。

需要注意的是,這個檔案系統資料將像其它記憶體頁一樣被快取起來。在隨後的I/O請求中,一些資料或所有檔案資料仍然儲存在實體記憶體中,可以直接重用不需要從磁碟重讀。

檔案鎖定

檔案加鎖是一種機制,一個程式可以阻止其它程式訪問一個檔案或限制其它程式訪問該檔案。雖然名為“檔案鎖定”,意味著鎖定整個檔案(經常做的)。鎖定通常可以在一個更細粒度的水平。隨著粒度下降到位元組級,檔案的區域通常會被鎖定。鎖與特定檔案相關聯,起始於檔案的指定位元組位置並執行到指定的位元組範圍。這一點很重要,因為它允許多個程式協作訪問檔案的特定區域而不妨礙別的程式在檔案其它位置操作。

檔案鎖有兩種形式:共享獨佔。多個共享鎖可以同時在相同的檔案區域有效。另一方面,獨佔鎖要求沒有其它鎖對請求的區域有效。

I/O

並非所有的I/O是面向塊的。還有流I/O,它是管道的原型,必須順序訪問I/O資料流的位元組。常見的資料流有TTY(控制檯)裝置、列印埠和網路連線。

資料流通常但不一定比塊裝置慢,提供間歇性輸入。大多數作業系統允許在非阻塞模式下工作。允許一個程式檢查資料流的輸入是否可用,不必在不可用時發生阻塞。這種管理允許程式在輸入到達時進行處理,在輸入流空閒時可以執行其他功能。

比非阻塞模式更進一步的是有條件的選擇(readiness selection)。它類似於非阻塞模式(並且通常建立在非阻塞模式基礎上),但是減輕了作業系統檢查流是否就緒準的負擔。作業系統可以被告知觀察流集合,並向程式返回哪個流準備好的指令。這種能力允許程式通過利用作業系統返回 的準備資訊,使用通用程式碼和單個執行緒複用多個活動流。這種方式被廣泛用於網路伺服器,以便處理大量的網路連線。準備選擇對於大容量擴充套件是至關重要的。

到此為止,對這個非常複雜的話題有一大堆技術術語。

如果你有想法和疑問,請給給我發評論。

學習快樂!!

相關文章