零拷贝、mmap、sendfile
目錄
- 零拷貝
- mmap
- sendFile
- 總結(jié)
零拷貝
要了解零拷貝,首先得先了解一下傳統(tǒng) IO 的執(zhí)行流程,這里舉個例子,通過傳統(tǒng)的 IO 進(jìn)行網(wǎng)絡(luò)傳輸來傳輸一個文件。
先上一張圖,這張圖就代表了傳統(tǒng) IO 傳輸文件的流程。
讀取文件的時候,會從用戶態(tài)切換為內(nèi)核態(tài),同時基于 DMA 引擎將磁盤文件拷貝到內(nèi)核緩沖區(qū)。
看到這里,可能你就已經(jīng)懵逼了,什么是用戶態(tài)和內(nèi)核態(tài),什么是 DMA 拷貝,我用大白話解釋一下
首先用戶態(tài)其實就是 CPU 在執(zhí)行你的代碼,而內(nèi)核態(tài)呢,其實就是你沒有那個權(quán)限去操作硬件,所以只能交給系統(tǒng)去調(diào)用,這個時候就是內(nèi)核態(tài)。舉個例子,你的女朋友需要你修個電腦(醒醒,但凡有一粒花生米也不至于喝成這樣),我換個說法,假如你同班的女同學(xué)想讓你修個電腦,但是宿管阿姨不肯放你進(jìn)女生宿舍,這個時候你就是用戶態(tài),你不能進(jìn)女生宿舍,所以你只能讓宿管阿姨(內(nèi)核態(tài))來幫你把電腦取出來。
那什么是 DMA 拷貝呢,DMA(DirectMemoryAccess,直接內(nèi)存存取)其實就是因為 CPU 老哥太累了,所以找了個小弟,就是 DMA 替他完成一部分的拷貝工作,這樣 CPU 就能去做其他事情了。
講完了內(nèi)核態(tài)和用戶態(tài)還有 DMA 的大概意思,我們接著回到剛才的 IO 流程中,第一步我們將文件從磁盤文件讀到了用戶緩沖區(qū),此時經(jīng)歷了一次上下文切換和一次拷貝。
由內(nèi)核態(tài)切換為用戶態(tài),基于 CPU 把內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到用戶緩沖區(qū)。
調(diào)用 socket 的輸出流的 write 方法的話,此時會從用戶態(tài)切換到內(nèi)核態(tài),同時基于 CPU把用戶緩沖區(qū)里的數(shù)據(jù)拷貝到 Socket 緩沖區(qū)里去,接著會有一個異步化的過程,基于 DMA 引擎從 Socket 緩沖區(qū)里把數(shù)據(jù)拷貝到網(wǎng)絡(luò)協(xié)議引擎里發(fā)送出去。
當(dāng) IO 操作完成之后,又從內(nèi)核態(tài)切換為用戶態(tài)。
通過上面的步驟可以發(fā)現(xiàn)傳統(tǒng)的 IO 操作執(zhí)行,有 4 次上下文的切換和 4 次拷貝,是不是很繁瑣。
零拷貝的話,一般有 mmap 和 sendFile 兩種,一個一個來說。
mmap
mmap 是一種內(nèi)存映射技術(shù),mmap 相比于傳統(tǒng)的 IO 來說,其實就是少了 1 次 CPU 拷貝而已,上圖。
傳統(tǒng) IO 里面從內(nèi)核緩沖區(qū)到用戶緩沖區(qū)有一次 CPU 拷貝,從用戶緩沖區(qū)到 Socket 緩沖區(qū)又有一次 CPU 拷貝。mmap 則一步到位,直接基于 CPU 將內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到了 Socket 緩沖區(qū)。
之所以能夠減少一次拷貝,就是因為 mmap 直接將磁盤文件數(shù)據(jù)映射到內(nèi)核緩沖區(qū),這個映射的過程是基于 DMA 拷貝的,同時用戶緩沖區(qū)是跟內(nèi)核緩沖區(qū)共享一塊映射數(shù)據(jù)的,建立共享映射之后,就不需要從內(nèi)核緩沖區(qū)拷貝到用戶緩沖區(qū)了。
雖然減少了一次拷貝,但是上下文切換的次數(shù)還是沒變。
RocketMQ 中就是使用的 mmap 來提升磁盤文件的讀寫性能。
sendFile
在 Linux 中,提供 sendFile 函數(shù),實現(xiàn)了零拷貝,依舊是先上圖。
可以看到在圖中,已經(jīng)沒有了用戶緩沖區(qū),因為用戶緩沖區(qū)是在用戶空間的,所以沒有了用戶緩沖區(qū)也就意味著不需要上下文切換了,就省略了這一步的從內(nèi)核態(tài)切換為用戶態(tài)。
同時也不需要基于 CPU 將內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到 Socket 緩沖區(qū)了,只需要從內(nèi)核緩沖區(qū)拷貝一些 offset 和 length 到 Socket 緩沖區(qū)。
接著從內(nèi)核態(tài)切換到用戶態(tài),從內(nèi)核緩沖區(qū)直接把數(shù)據(jù)拷貝到網(wǎng)絡(luò)協(xié)議引擎里去;同時從 Socket 緩沖區(qū)里拷貝一些 offset 和 length 到網(wǎng)絡(luò)協(xié)議引擎里去,但是這個 offset 和 length 的量很少,幾乎可以忽略。
sendFile 整個過程只有兩次上下文切換和兩次 DMA 拷貝,很重要的一點是這里完全不需要 CPU 來進(jìn)行拷貝了,所以才叫做零拷貝,這里的拷貝指的就是操作系統(tǒng)的層面。
那你肯定會問,那 mmap 里面有一次 CPU 拷貝為啥也算零拷貝,只能說那不算是嚴(yán)格意義上的零拷貝,但是他確實是優(yōu)化了普通 IO 的執(zhí)行流程,就像老婆餅里也沒有老婆嘛。
Kafka 和 Tomcat 內(nèi)部使用就是 sendFile 這種零拷貝。
總結(jié)
傳統(tǒng) IO 執(zhí)行的話需要 4 次上下文切換(用戶態(tài) -> 內(nèi)核態(tài) -> 用戶態(tài) -> 內(nèi)核態(tài) -> 用戶態(tài))和 4 次拷貝(磁盤文件 DMA 拷貝到內(nèi)核緩沖區(qū),內(nèi)核緩沖區(qū) CPU 拷貝到用戶緩沖區(qū),用戶緩沖區(qū) CPU 拷貝到 Socket 緩沖區(qū),Socket 緩沖區(qū) DMA 拷貝到協(xié)議引擎)。
mmap 將磁盤文件映射到內(nèi)存,支持讀和寫,對內(nèi)存的操作會反映在磁盤文件上,適合小數(shù)據(jù)量讀寫,需要 4 次上下文切換(用戶態(tài) -> 內(nèi)核態(tài) -> 用戶態(tài) -> 內(nèi)核態(tài) -> 用戶態(tài))和3 次拷貝(磁盤文件DMA拷貝到內(nèi)核緩沖區(qū),內(nèi)核緩沖區(qū) CPU 拷貝到 Socket 緩沖區(qū),Socket 緩沖區(qū) DMA 拷貝到協(xié)議引擎)。
總結(jié)
以上是生活随笔為你收集整理的零拷贝、mmap、sendfile的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一只贵宾犬多少钱啊?
- 下一篇: 我选激光电视,国产的哪家好啊?