蚂蚁金服二面:面试官问我零拷贝的实现原理,当场跪。。。
本文來源:占小狼的博客
"狼哥,面試又跪了,碰到了知識盲區"
"哪個?"
"一面還可以,二面面試官問我零拷貝的原理,懵逼了...這塊內容沒去研究過"
"哦,這個知識點,我之前應該有講過,你沒注意到?"
"這東西工作中用不到,可能被我忽略了"
"嘖嘖嘖..."
"哎,有空和我講講?"
"先從簡單開始,實現下這個場景:從一個文件中讀出數據并將數據傳到另一臺服務器上?"
"為啥寫這個?"
"你先寫"
"行..."
1分鐘后
"我寫了偽代碼"
File.read(file, buf, len); Socket.send(socket, buf, len);"這里涉及到了幾次數據拷貝?"
"2次?磁盤拷貝到內存,內存拷貝到Socket?"
"emmm,怪不得掛了,不冤"
"這種方式一共涉及了4次數據拷貝,知道用戶態和內核態的區別嗎?"
"了解"
"行,文字有點干癟,你先看這個圖"
1、應用程序中調用 read()?方法,這里會涉及到一次上下文切換(用戶態->內核態),底層采用DMA(direct memory access)讀取磁盤的文件,并把內容存儲到內核地址空間的讀取緩存區。
2、由于應用程序無法訪問內核地址空間的數據,如果應用程序要操作這些數據,得把這些內容從讀取緩沖區拷貝到用戶緩沖區。
?read()?調用的返回引發一次上下文切換(內核態->用戶態),現在數據已經被拷貝到了用戶地址空間緩沖區,如果有需要,可以操作修改這些內容。
3、我們最終目的是把這個文件內容通過Socket傳到另一個服務中,調用Socket的 send()方法,又涉及到一次上下文切換(用戶態->內核態)
同時,文件內容被進行第三次拷貝,這次的緩沖區與目標套接字相關聯,與讀取緩沖區無關。
4、 send()調用返回,引發第四次的上下文切換,同時進行第四次拷貝,DMA把數據從目標套接字相關的緩存區傳到協議引擎進行發送。
"整個過程中,過程1和4是由DMA負責,并不會消耗CPU,只有過程2和3的拷貝需要CPU參與"
整明白了?
"我消化一下..."
半小時后...
"狼哥,感覺這個過程中好幾次的拷貝都是多余的,很影響性能啊"
"對,所以才有了零拷貝技術"
"具體咋實現?"
"慢慢來,如果在應用程序中,不需要操作內容,過程2和3顯然是多余的,如果可以直接把內核態讀取緩存沖區數據直接拷貝到套接字相關的緩存區,是不是可以達到目的?"
這種實現,可以有以下幾點改進:
-
上下文切換的次數從四次減少到了兩次
-
拷貝次數從四次減少到了三次(其中DMA copy 2次,CPU copy 1次)
"怎么實現?"
"在Java中,FileChannel的transferTo() 方法可以實現這個過程,該方法將數據從文件通道傳輸到給定的可寫字節通道, 上面的 file.read()和 socket.send() 調用動作可以替換為 transferTo() 調用"
public void transferTo(long position, long count, WritableByteChannel target);在 UNIX 和各種 Linux 系統中,此調用被傳遞到 sendfile() 系統調用中,最終實現將數據從一個文件描述符傳輸到了另一個文件描述符。
"這樣確實改善了很多,但還沒達到零拷貝的要求(還有一次cpu參與的拷貝),還有其它黑技術?"
"對的,如果底層網絡接口卡支持收集操作的話,就可以進一步的優化。"
"怎么說?"
在 Linux 內核 2.4 及后期版本中,針對套接字緩沖區描述符做了相應調整,DMA自帶了收集功能,對于用戶方面,用法還是一樣,只是內部操作已經發生了改變:
具體過程:
transferTo() 方法使用 DMA 將文件內容拷貝到內核讀取緩沖區。
避免了內容的整體拷貝,只把包含數據位置和長度信息的描述符追加到套接字緩沖區,DMA 引擎直接把數據從內核緩沖區傳到協議引擎,從而消除了最后一次 CPU參與的拷貝動作。
總結
以上是生活随笔為你收集整理的蚂蚁金服二面:面试官问我零拷贝的实现原理,当场跪。。。的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Netty 在 Dubbo 中是如何应用
- 下一篇: Java中的享元设计模式,涨姿势了!