聊聊文件IO


前言java

在消息隊列中,消息消費利用零拷貝減小拷貝次數、採用內存映射技術來使文件的訪問像訪問內存那樣。那到底零拷貝原理是什麼呢?怎麼實現內存映射的?緩存

零拷貝數據結構

爲何使用零拷貝?使用零拷貝有什麼好處?零拷貝的意思是說不須要將數據從某處複製到特定的某一個區域,能夠減小CPU在數據複製的消耗還有內存內存帶寬。異步

例子:對於文件下載功能實現來講,不少狀況下很不斷地讀取文件內容到應用程序空間,再不斷地將數據寫到網卡發送給客戶端。socket

while((n = read(diskfd, buf, BUF_SIZE)) > 0)    
    write(sockfd, buf , n);複製代碼

對於以上的IO操做實際已經發生了屢次的數據拷貝。spa

數據拷貝流程:
3d

一、DMA將磁盤文件拷貝到內核緩存
code

二、CPU控制將內核緩存數據拷貝到應用程序空間orm

三、CPU控制將應用程序數據拷貝到套接字的buffer
cdn

四、DMA將socket的buffer拷貝到網卡設備發送出去

其實這種狀況下須要消耗兩次的CPU、DMA完成拷貝工做;對於應用程序來講,CPU資源是相對昂貴的,應該儘量節省,是否減小利用cpu資源完成拷貝?

採用sendfile減小內核空間到用戶空間的拷貝:

數據拷貝流程:

一、利用DMA拷貝磁盤數據到內核的buffer

二、在套接字buffer上追加當前要發送的數據在kernel buffer的位置還有偏移量

三、根據套接字buffer的位置還有偏移量信息,DMA將kernel buffer的數據拷貝到網卡發送出去

能夠看到採用sendfile只須要完成兩次拷貝,不須要拷貝到用戶空間,並且並不消耗cpu資源,能夠大幅提升程序的效率。可是因爲這次IO的整個流程都在內核空間完成,這種效率是最高的,可是應用程序沒法對文件資源進行操做,那該怎麼解決呢?有沒有什麼方式能夠即減小數據的拷貝,同時能夠操做數據呢?

採用mmap內存映射

mmap(內存映射)是一個比sendfile昂貴但優於傳統I/O的方法。

咱們的程序發起一次系統調用,將一個文件(或者文件的一部分)映射到虛擬地址空間的一部分,注意這時候沒有分配和映射到具體的物理內存空間,而是到第一次加載這個文件的時候,經過MMU把以前虛擬地址換算成物理地址,把文件加載進物理內存。


數據拷貝流程:

一、調用mmap,發生用戶空間到內核空間的上下文切換(第一次切換),經過DMA將磁盤數據拷貝到內核空間

二、mmap調用返回,發生內核空間到用戶空間的切換(第二次切換),而且用戶空間和內核空間共享這部分緩存區,用戶空間能夠像操做緩衝區的數據同樣操做這部分數據

三、調用write,發生用戶空間到內核空間的切換(第三次上下文切換),CPU拷貝內核空間的緩衝區數據到套接字的buffer

四、write調用返回,發生內核空間到用戶空間的切換(第四次上下文切換),而且經過DMA將套接字的buffer拷貝到網卡發送出去

能夠看到採用mmap發送數據,能夠操做數據,發生四次的上下文切換還有3次的數據拷貝,可是明顯優於傳統的IO。

實現零複製的軟件一般依靠基於直接存儲器訪問(Direct Memory Access,DMA)的複製,以及經過內存管理單元(MMU)的內存映射。這些功能須要特定硬件的支持,並一般涉及到特定存儲器的對齊。

不少同窗在看消息隊列的刷盤時候會看到按照4k的page cache來實現異步刷盤,那page cache具體實現是什麼呢?爲何它能夠提升刷盤效率?


Page Cache

page cache的目的是經過將數據存儲在物理內存使磁盤IO最小化。page cache的大小是動態的,能夠增大到消耗全部的free memory, 能夠縮小來減輕內存壓力。在page cache中的page能夠包含許多不連續的物理disk blocks。

在 Linux 內核中,文件的每一個數據塊最多隻能對應一個 Page Cache 項,它經過兩個數據結構來管理這些 Cache 項,一個是 radix tree,另外一個是雙向鏈表。Radix tree 是一種搜索樹,Linux 內核利用這個數據結構來經過文件內偏移快速定位 Cache 項。

實際使用page cache預讀取來提升順序讀的效率,利用預先加載數據減小磁盤io,可是對於隨機讀顯然採用page cache是不那麼合適的,由於屢次的預加載反而下降讀取的效率。

在前面的分析中,只針對數據拷貝效率進行說明,而對於文件數據怎麼讀取到內核空間呢?block io layer 對外提供通用的磁盤訪問接口,而block layer再往下就是某個device具體的driver用於加載磁盤的數據。

在調用filechannel的read加載的時候會觸發page cache預讀IO的方式,隨着讀取預讀範圍擴大直到佔滿空閒內存,這種方式能提升順序讀取的效率,page cache頗有效果。而對於隨機讀取的方式,依舊存在其價值,減小了 Block IO Layed(近似理解爲磁盤) 到 Page Cache 的 overhead。

                                   

                        歡迎關注【路上小棧】,優質內容持續更新!

相關文章
相關標籤/搜索