首頁 >Java >java教程 >Java I/O底層是如何運作的?

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

伊谢尔伦
伊谢尔伦原創
2016-11-26 09:10:031161瀏覽

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

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

 緩存處理和內核vs用戶空間

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

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

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

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

 虛擬記憶體

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

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

多個虛擬位址可以對應到相同的實體位址。

一個虛擬位址空間可以大於實際可用硬體記憶體。

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

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

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

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

 記憶體分頁

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

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

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

 面向檔案、區塊I/O

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

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

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

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

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

確定請求跨越了哪些檔案系統分頁(磁碟段的集合)。磁碟上的檔案內容及元資料可能分佈在多個檔案系統頁面上,這些頁面可能是不連續的。

分配足夠多的核心空間記憶體頁面來保存相同的檔案系統頁面。

建立這些記憶體分頁與磁碟上檔案系統分頁的對應。

對每個記憶體分頁產生分頁錯誤。

虛擬記憶體系統陷入分頁錯誤並且調度pagins(頁面調入),透過從磁碟讀取內容來驗證這些頁面。

一旦pageins完成,檔案系統會分解原始資料來提取要求的檔案內容或屬性資訊。

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

 檔案鎖定

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

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

 流I/O

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

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

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


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn