作 者: 範桂颶
is-cloud.bg.csdn.net/article/details/105897963
傳統的 System Call I/O
在 Linux 系統中,傳統的存取方式是透過 write() 和 read() 兩個系統呼叫實作的,透過 read() 函式讀取檔到到緩存區中,然後透過 write() 方法把緩存中的數據輸出到網路埠。
read(file_fd, tmp_buf, len);
write(socket_fd, tmp_buf, len);
下圖分別對應傳統 I/O 操作的數據讀寫流程,整個過程涉及 2 次 CPU 拷貝、2 次 DMA 拷貝,總共 4 次拷貝,以及 4 次上下文切換。
CPU 拷貝 :
由 CPU 直接處理數據的傳送,數據拷貝時會一直占用 CPU 的資源。
DMA 拷貝 :
由 CPU 向DMA磁盤控制器下達指令,讓 DMA 控制器來處理數據的傳送,數據傳送完畢再把資訊反饋給 CPU,從而減輕了 CPU 資源的占有率。
上下文切換 :
當使用者程式向內核發起系統呼叫時,CPU 將使用者行程從使用者態切換到內核態;
當系統呼叫返回時,CPU 將使用者行程從內核態切換回使用者態。
讀操作
當應用程式執行 read 系統呼叫讀取一塊數據的時候,如果這塊數據已經存在於使用者行程的頁記憶體中,就直接從記憶體中讀取數據。
如果數據不存在,則先將數據從磁盤載入數據到內核空間的讀緩存(Read Buffer)中,再從讀緩存拷貝到使用者行程的頁記憶體中。
read(file_fd, tmp_buf, len);
基於傳統的 I/O 讀取方式,read 系統呼叫會觸發 2 次上下文切換,1 次 DMA 拷貝和 1 次 CPU 拷貝。
發起數據讀取的流程如下:
使用者行程透過 read() 函式向 Kernel 發起 System Call,上下文從 user space 切換為 kernel space。
CPU 利用 DMA 控制器將數據從主記憶體或硬碟拷貝到 kernel space 的讀緩沖區(Read Buffer)。
CPU 將讀緩沖區(Read Buffer)中的數據拷貝到 user space 的使用者緩沖區(User Buffer)。
上下文從 kernel space 切換回使用者態(User Space),read 呼叫執行返回。
寫操作
當應用程式準備好數據,執行 write 系統呼叫發送網路數據時,先將數據從使用者空間的頁緩存拷貝到內核空間的網路緩沖區(Socket Buffer)中,然後再將寫緩存中的數據拷貝到網卡裝置完成數據發送。
write(socket_fd, tmp_buf, len);
基於傳統的 I/O 寫入方式,write() 系統呼叫會觸發 2 次上下文切換,1 次 CPU 拷貝和 1 次 DMA 拷貝。
使用者程式發送網路數據的流程如下:
使用者行程透過 write() 函式向 kernel 發起 System Call,上下文從 user space 切換為 kernel space。
CPU 將使用者緩沖區(User Buffer)中的數據拷貝到 kernel space 的網路緩沖區(Socket Buffer)。
CPU 利用 DMA 控制器將數據從網路緩沖區(Socket Buffer)拷貝到 NIC 進行數據傳輸。
上下文從 kernel space 切換回 user space,write 系統呼叫執行返回。
網路 I/O
磁盤 I/O
高效能最佳化的 I/O
零拷貝技術。
多路復用技術。
頁緩存(PageCache)技術。
其中, 頁緩存(PageCache) 是作業系統對檔的緩存,用來減少對磁盤的 I/O 操作,以頁為單位的,內容就是磁盤上的物理塊,頁緩存能幫助程式對檔進行順序讀寫的速度幾乎接近於記憶體的讀寫速度,主要原因就是由於 OS 使用 PageCache 機制對讀寫存取操作進行了效能最佳化。微信搜尋公眾號:架構師指南,回復:架構師 領取資料 。
頁緩存讀取策略 :當行程發起一個讀操作 (比如,行程發起一個 read() 系統呼叫),它首先會檢查需要的數據是否在頁緩存中:
如果在 ,則放棄存取磁盤,而直接從頁緩存中讀取。
如果不在 ,則內核排程塊 I/O 操作從磁盤去讀取數據,並讀入緊隨其後的少數幾個頁面(不少於一個頁面,通常是三個頁面),然後將數據放入頁緩存中。
頁緩存寫策略 :當行程發起 write 系統呼叫寫數據到檔中,先寫到頁緩存,然後方法返回。此時數據還沒有真正的保存到檔中去,Linux 僅僅將頁緩存中的這一頁數據標記為 「臟」,並且被加入到臟頁連結串列中。
然後,由 flusher 回寫執行緒周期性將臟頁連結串列中的頁寫到磁盤,讓磁盤中的數據和記憶體中保持一致,最後清理「臟」標識。在以下三種情況下,臟頁會被寫回磁盤:
空閑記憶體低於一個特定閾值。
臟頁在記憶體中駐留超過一個特定的閾值時。
當使用者行程呼叫 sync() 和 fsync() 系統呼叫時。
儲存裝置的 I/O 棧
由圖可見,從系統呼叫的介面再往下,Linux 下的 IO 棧致大致有三個層次:
檔案系統層 ,以 write 為例,內核拷貝了 write 參數指定的使用者態數據到檔案系統 Cache 中,並適時向下層同步。
塊層 ,管理塊裝置的 IO 佇列,對 IO 請求進行合並、排序(還記得作業系統課程學習過的 IO 排程演算法嗎?)。
裝置層 ,透過 DMA 與記憶體直接互動,完成數據和具體裝置之間的互動。
結合這個圖,想想 Linux 系統編程裏用到的 Buffered IO 、 mmap 、 Direct IO ,這些機制怎麽和 Linux I/O 棧 聯系起來呢?上面的圖有點復雜,我畫一幅簡圖,把這些機制所在的位置添加進去:
Linux IO系統
這下一目了然了吧? 傳統的 Buffered IO 使用 read 讀取檔的過程什麽樣的?假設要去讀一個冷檔(Cache 中不存在),open 開啟檔內核後建立了一系列的數據結構,接下來呼叫 read,到達檔案系統這一層,發現 Page Cache 中不存在該位置的磁盤對映,然後建立相應的 Page Cache 並和相關的磁區關聯。
然後請求繼續到達塊裝置層,在 IO 佇列裏排隊,接受一系列的排程後到達裝置驅動層,此時一般使用 DMA 方式讀取相應的磁盤磁區到 Cache 中,然後 read 拷貝數據到使用者提供的 使用者態 buffer 中去(read 的參數指出的)。
整個過程有幾次拷貝? 從磁盤到 Page Cache 算第一次的話,從 Page Cache 到使用者態 buffer 就是第二次了。而 mmap 做了什麽?mmap 直接把 Page Cache 對映到了使用者態的地址空間裏了,所以 mmap 的方式讀檔是沒有第二次拷貝過程的。
那 Direct IO 做了什麽? 這個機制更狠,直接讓使用者態和塊 IO 層對接,直接放棄 Page Cache,從磁盤直接和使用者態拷貝數據。好處是什麽?寫操作直接對映行程的buffer到磁盤磁區,以 DMA 的方式傳輸數據,減少了原本需要到 Page Cache 層的一次拷貝,提升了寫的效率。
對於讀而言,第一次肯定也是快於傳統的方式的,但是之後的讀就不如傳統方式了(當然也可以在使用者態自己做 Cache,有些商用資料庫就是這麽做的)。
除了傳統的 Buffered IO 可以比較自由的用偏移+長度的方式讀寫檔之外, mmap 和 Direct IO 均有數據按頁對齊的要求,Direct IO 還限制讀寫必須是底層儲存裝置塊大小的整數倍(甚至 Linux 2.4 還要求是檔案系統邏輯塊的整數倍)。
所以介面越來越底層,換來表面上的效率提升的背後,需要在應用程式這一層做更多的事情。所以想用好這些高級特性,除了深刻理解其背後的機制之外,也要在系統設計上下一番功夫。
I/O Buffering
如圖,當程式呼叫各類檔操作函式後,使用者數據(User Data)到達磁盤(Disk)的流程如圖所示。
圖中描述了 Linux 下檔操作函式的層級關系和記憶體緩存層的存在位置。中間的黑色實線是使用者態和內核態的分界線。
從上往下分析這張圖:
1. 首先是 C 語言 stdio 庫定義的相關檔操作函式,這些都是使用者態實作的跨平台封裝函式。stdio 中實作的檔操作函式有自己的 stdio buffer,這是在使用者態實作的緩存。此處使用緩存的原因很簡單 — 系統呼叫總是昂貴的。如果使用者程式碼以較小的 size 不斷的讀或寫檔的話,stdio 庫將多次的讀或者寫操作透過 buffer 進行聚合是可以提高程式執行效率的。
stdio 庫同時也支持 fflush 函式來主動的重新整理 buffer,主動的呼叫底層的系統呼叫立即更新 buffer 裏的數據。特別地,setbuf 函式可以對 stdio 庫的使用者態 buffer 進行設定,甚至取消 buffer 的使用。
2. 系統呼叫的 read/write 和真實的磁盤讀寫之間也存在一層 buffer ,這裏用術語 Kernel buffer cache 來指代這一層緩存。
在 Linux 下,檔的緩存習慣性的稱之為 Page Cache,而更低一級的裝置的緩存稱之為 Buffer Cache。
這兩個概念很容易混淆,這裏簡單的介紹下概念上的區別:Page Cache 用於緩存檔的內容,和檔案系統比較相關。檔的內容需要對映到實際的物理磁盤,這種對映關系由檔案系統來完成;Buffer Cache 用於緩存儲存裝置塊(比如磁盤磁區)的數據,而不關心是否有檔案系統的存在(檔案系統的後設資料緩存在 Buffer Cache 中)。
-End-
讀到這裏說明你喜歡本公眾號的文章,歡迎 置頂(標星)本公眾號 Linux技術迷,這樣就可以第一時間獲取推播了~
在本公眾號,後台回復:Linux,領取2T學習資料 !
推薦閱讀
1.
2.
3.
4.