在普通的IO拷貝時要有4次的上下文切換過程和4次的拷貝過程,在高並行的場景下對效能會產生比較大的影響。
資料在讀寫的過程中都需要CPU發出對應的命令來完成,因為CPU的速度比IO操作要快的多,在資料拷貝的過程中CPU不可能一直處於等待的過程,但是如果不等,CPU又不知道IO什麼時候處理完,為了協調高速的CPU與低速的IO的矛盾,因此引入了DMA,DMA(Direct Memory Access)直接記憶體存取技術,通過它來進行記憶體和IO裝置的資料傳輸。大至就是CPU給DMA發一個指令,然後DMA開始幹活,然後CPU幹別的活,DMA幹完後告訴CPU,從而減少了CPU的等待時間。
觀察圖可以2.1 和2.2 感覺資料在此過程做了個無用功,從核心態搬到使用者態,再由使用者態搬到核心態,因此提出了零值拷貝,零值拷貝不是沒有拷貝,而是不再有使用者態和核心態間資料拷貝過程,他的實現有mmap和sendfile兩種方式。
mmap (member map)記憶體對映,資料讀取到系統快取區後,使用者態資料在系統快取區的對映就可以完成資料的傳輸過程。
mmap的操作共有4個上下文的切換和3次的資料拷貝過程,比普通的拷貝少了一次cpu的拷貝。
mmap的拷貝方法要先獲取資料區的對映,然後有使用者發起寫資料的命令後,在往網路卡上寫,如果不需要對資料做任何處理,只是原封不動的把資料傳送出去,mmap命令和write命令可以合併為一個命令,直接告訴作業系統往外傳送哪個資料即可,合併後的命令就sendfile。
sendfile的過程由2次上下文的切換和3次的資料拷貝過程,整個過程讀到的資料對使用者空間不可見,適應用靜態檔案伺服器。
在sendfile的cpu把資料從快取區從一個地方拷貝到另一個地方,也會浪費額外的空間,能不能在往網路卡上寫資料時只用第一次DMA拷貝出來的資料,直接拷貝到網路卡上呢?因此對sendfile再次升級,引入了DMA Scatter/Gather 分散/收集功能。
此過程對使用者空間仍然是不可見的,而且需要硬體的支援。有2次上下文切換和2次的拷貝過程,效能在大幅的提高。
kafka和rocketmq都用到了mmap技術,兩個中介軟體都有訊息的持久化過程,和訊息的讀取過程。
mq對訊息持久化和讀取的過程都用到了mmap+write,而kafka則是持久化的過程用到了mmap+write,讀取訊息用的sendfile。
DMA是為了解決高速CPU和低速IO操作的矛盾,cpu發起一個讀取指令後,由dma接管資料的讀取複製工作,cpu處理別的事件。
名稱 | 過程 | 上下文切換次數 | 拷貝次數 |
---|---|---|---|
傳統IO | 讀取命令、從磁碟讀、寫到快取、寫到使用者快取區、寫到快取區,寫到網路卡 | 4 | 4 |
mmap+write | mmap命令,從磁碟讀,寫到快取、返回mmap、發起write命令,從快取讀,寫到另一快取,寫到網路卡 | 4 | 3 |
sendfile | sendfile命令,從磁碟讀,寫快取區,寫到另一快取區,寫網路卡 | 2 | 3 |
sendfile升級版 | sendfile命令,從磁碟讀,寫快取區,根據檔案描述符從快取區讀,寫網路卡 | 2 | 2 |