Java IO 學習(三)緩沖IO / 直接IO / 內存映射


緩沖IO

在介紹緩沖IO之前需要先了解一下常用的機械硬盤的原理與特點

一個機械硬盤中裝有多個盤片

每個盤片上有多個同心圓(磁道)

每個同心圓又由多個弧(扇區)組成,每個弧上都記錄了等量的數據(比方說512byte)

如果發起一個隨機讀寫請求,磁頭需要先找到對應的磁道,然后等待對應的扇區旋轉到磁頭正下方才能開始讀取數據(民用機械硬盤的轉速一般在5400或者7200RPM,工業界倒是經常使用10000RPM的機械硬盤。但是它們的尋道時間大概都在幾ms到十幾ms左右)

機械硬盤的順序讀寫很快(一般在100-200MB/s),但是隨機讀寫很慢(尋道時間在十幾ms,導致隨機讀寫的iops只有幾十)

假定我們不做任何額外的優化處理,在用戶發起讀數據請求的時候,直接調用硬盤驅動讀取磁盤數據並返回

設想一個場景:循環調用read方法讀取文件,但是每次只讀取較少的數據(比方說每次只讀一個byte)。那么每次read請求都對應於一次對磁盤的隨機讀寫(兩次讀請求之前需要重新尋道),也就是說read操作的tps只有幾十。

也就是說此時磁盤占用率為100%,但是只能提供不到100byte/s的數據讀取率,這顯然是不可接受的。

 

Linux對此有個很簡單的優化,就是在內核中維護一塊緩沖區(buffer cache),在用戶第一次調用read讀取數據的時候,無論用戶想要讀取的數據有多小,都會一次性從磁盤中加載一段數據放到緩沖區中,這樣用戶下一次調用read方法的時候可以直接從緩沖區中返回數據,不用再次訪問磁盤了。

write方法也是同理,用戶寫入的數據不是直接落盤,而是先寫到kernel中的緩沖區里,按照一定的策略批量刷盤。當然也可以調用flush方法強制將緩存區的數據落盤。

 

這個優化極大的提高了順序讀寫的效率。由於直接讀寫的是kernel中的緩沖區而不是磁盤,這種IO被稱為緩沖IO。

 

直接IO

一般來說,上面介紹的緩沖IO已經足夠應付日常需求了。但是像數據庫這種極度依賴IO的應用程序,為了追求極致的性能,往往更加願意自己直接操作磁盤。

直接IO可以直接將數據從磁盤復制到用戶空間,或者將數據從用戶空間寫到磁盤,減少了kernel中的緩沖區這一環節,這是直接IO可以提高性能的原理。

但是如果用得不好就悲劇了,所以直接IO只在少數場景下使用。

 

內存映射

先給出mmap的官方文檔

mmap方法會返回一個void *類型的指針ptr,它指向進程邏輯空間中的一個地址。

后續如果想要讀寫文件,無需調用read/write方法, 而是直接操作這個ptr指針即可。

用戶試圖向ptr指針指向的空間讀寫數據時,由於MMU無法在物理內存中找到對應的地址,會觸發一次缺頁中斷,OS會去硬盤中找到對應的數據並復制到內存中,然后用戶就能正常完成讀寫操作了。這個過程是由操作系統自動完成的。

 

為什么說內存映射效率比緩沖IO要高?

我們回憶一下緩沖IO的工作流程:

1. 用戶調用read方法

2. 調用系統調用,觸發中斷,進程從用戶態進入內核態

3. 從硬盤中讀取數據並復制到kernel緩沖區

4. 將數據從kernel緩存區復制到用戶提供的byte數組中

5. 進程從內核態返回到用戶態

完成

從上面的流程中我們可以看到,調用一次read方法,最多可能會引起兩次用戶態與內核態之間的切換,以及兩次數據復制

 

而內存映射呢?

1. 用戶試圖訪問ptr指向的數據

2. MMU解析失敗,觸發缺頁中斷,程序從用戶態進入到內核態

3. 從硬盤中讀取數據並復制到進程空間中ptr指向的邏輯空間里

4. 進程從內核態返回到用戶態

完成

可以看出,試圖訪問內存映射文件,最多可能會引起兩次用戶態與內核態之間的切換,以及一次數據復制

也就是說,內存映射與緩沖IO相比,可以節省數據復制帶來的開銷,因此效率較高。

 

參考資料

Linux 中直接 I/O 機制的介紹

內存映射文件原理探索


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM