深入理解NIO零拷貝及用戶空間與內(nèi)核空間切換

1、什么是零拷貝

維基上是這么描述零拷貝的:零拷貝描述的是CPU不執(zhí)行拷貝數(shù)據(jù)從一個(gè)存儲(chǔ)區(qū)域到另一個(gè)存儲(chǔ)區(qū)域的任務(wù)盛卡,這通常用于通過網(wǎng)絡(luò)傳輸一個(gè)文件時(shí)以減少CPU周期和內(nèi)存帶寬渠概。

2、零拷貝給我們帶來的好處

  • 1、減少甚至完全避免不要的CPU拷貝曙痘,讓CPU解脫出來去執(zhí)行其他的任務(wù)
  • 2、減少內(nèi)存帶寬的占用
  • 3立肘、通常零拷貝技術(shù)還能夠減少用戶空間和操作系統(tǒng)內(nèi)核空間的上下文切換

3边坤、 零拷貝的實(shí)現(xiàn)

  • 實(shí)際的實(shí)現(xiàn)并沒有真正的標(biāo)準(zhǔn),取決于操作系統(tǒng)如何實(shí)現(xiàn)這一點(diǎn)谅年,零拷貝本質(zhì)完全依賴操作系統(tǒng)惩嘉,操作系統(tǒng)支持就有,不支持就沒有踢故,不依賴Java本身(HeapByteBuffer,DirectByteBuffer)文黎,NIO只是解決部分拷貝過程、
  • 零拷貝實(shí)現(xiàn)了內(nèi)存的數(shù)據(jù)重用性 減少拷貝過程次數(shù)來提高性能

4殿较、傳統(tǒng)I/O

  • 在Java中耸峭,我們可以通過InputStream從源數(shù)據(jù)中讀取數(shù)據(jù)流到一個(gè)緩沖區(qū)里,然后再將它們輸入到OutputStream里淋纲。我們知道劳闹,這種IO方式傳輸效率是比較低的。那么,當(dāng)使用上面的代碼時(shí)操作系統(tǒng)會(huì)發(fā)生什么情況:


    傳統(tǒng)I/O

1本涕、JVM發(fā)出read() 系統(tǒng)調(diào)用业汰。
2、OS上下文切換到內(nèi)核模式(第一次上下文切換)并將數(shù)據(jù)讀取到內(nèi)核空間緩沖區(qū)菩颖。(第一次拷貝:hardware ----> kernel buffer)
3样漆、OS內(nèi)核然后將數(shù)據(jù)復(fù)制到用戶空間緩沖區(qū)(第二次拷貝: kernel buffer --> user buffer),然后read系統(tǒng)調(diào)用返回晦闰。而系統(tǒng)調(diào)用的返回又會(huì)導(dǎo)致一次內(nèi)核空間到用戶空間的上下文切換(第二次上下文切換)放祟。
4、JVM處理代碼邏輯并發(fā)送write()系統(tǒng)調(diào)用呻右。
5跪妥、OS上下文切換到內(nèi)核模式(第三次上下文切換)并從用戶空間緩沖區(qū)復(fù)制數(shù)據(jù)到內(nèi)核空間緩沖區(qū)(第三次拷貝: user buffer ——> kernel buffer)。
6声滥、write系統(tǒng)調(diào)用返回眉撵,導(dǎo)致內(nèi)核空間到用戶空間的再次上下文切換(第四次上下文切換)。將內(nèi)核空間緩沖區(qū)中的數(shù)據(jù)寫到hardware(第四次拷貝: kernel buffer ——> hardware)落塑。

  • 總的來說执桌,傳統(tǒng)的I/O操作進(jìn)行了4次用戶空間與內(nèi)核空間的上下文切換,以及4次數(shù)據(jù)拷貝芜赌。顯然在這個(gè)用例中仰挣,從內(nèi)核空間到用戶空間內(nèi)存的復(fù)制是完全不必要的,因?yàn)槌藢?shù)據(jù)轉(zhuǎn)儲(chǔ)到不同的buffer之外缠沈,我們沒有做任何其他的事情膘壶。所以,我們能不能直接從hardware讀取數(shù)據(jù)到kernel buffer后洲愤,再從kernel buffer寫到目標(biāo)地點(diǎn)不就好了颓芭。為了解決這種不必要的數(shù)據(jù)復(fù)制,操作系統(tǒng)出現(xiàn)了零拷貝的概念柬赐。注意亡问,不同的操作系統(tǒng)對零拷貝的實(shí)現(xiàn)各不相同。在這里我們介紹linux下的零拷貝實(shí)現(xiàn)肛宋。

5州藕、通過sendfile實(shí)現(xiàn)的零拷貝I/O

  • sendfile

1、發(fā)出sendfile系統(tǒng)調(diào)用酝陈,導(dǎo)致用戶空間到內(nèi)核空間的上下文切換(第一次上下文切換)床玻。通過DMA將磁盤文件中的內(nèi)容拷貝到內(nèi)核空間緩沖區(qū)中(第一次拷貝: hard driver ——> kernel buffer)。
2沉帮、然后再將數(shù)據(jù)從內(nèi)核空間緩沖區(qū)拷貝到內(nèi)核中與socket相關(guān)的緩沖區(qū)中(第二次拷貝: kernel buffer ——> socket buffer)锈死。
3贫堰、sendfile系統(tǒng)調(diào)用返回,導(dǎo)致內(nèi)核空間到用戶空間的上下文切換(第二次上下文切換)待牵。通過DMA引擎將內(nèi)核空間socket緩沖區(qū)中的數(shù)據(jù)傳遞到協(xié)議引擎(第三次拷貝: socket buffer ——> protocol engine)其屏。

  • 通過sendfile實(shí)現(xiàn)的零拷貝I/O只使用了2次用戶空間與內(nèi)核空間的上下文切換,以及3次數(shù)據(jù)的拷貝缨该。你可能會(huì)說操作系統(tǒng)仍然需要在內(nèi)核內(nèi)存空間中復(fù)制數(shù)據(jù)(kernel buffer —>socket buffer)偎行。 是的,但從操作系統(tǒng)的角度來看压彭,這已經(jīng)是零拷貝,因?yàn)闆]有數(shù)據(jù)從內(nèi)核空間復(fù)制到用戶空間渗常。 內(nèi)核需要復(fù)制的原因是因?yàn)橥ㄓ糜布﨑MA訪問需要連續(xù)的內(nèi)存空間(因此需要緩沖區(qū))壮不。 但是,如果硬件支持scatter-and-gather皱碘,這是可以避免的询一。

6、帶有DMA收集拷貝功能的sendfile實(shí)現(xiàn)的I/O

DMA
  • 1癌椿、發(fā)出sendfile系統(tǒng)調(diào)用健蕊,導(dǎo)致用戶空間到內(nèi)核空間的上下文切換(第一次上下文切換)。通過DMA引擎將磁盤文件中的內(nèi)容拷貝到內(nèi)核空間緩沖區(qū)中(第一次拷貝: hard drive —> kernel buffer)踢俄。

  • 2缩功、沒有數(shù)據(jù)拷貝到socket緩沖區(qū)。取而代之的是只有相應(yīng)的描述符信息會(huì)被拷貝到相應(yīng)的socket緩沖區(qū)當(dāng)中都办。該描述符包含了兩方面的信息:a)kernel buffer的內(nèi)存地址嫡锌;b)kernel buffer的偏移量。

  • 3琳钉、sendfile系統(tǒng)調(diào)用返回势木,導(dǎo)致內(nèi)核空間到用戶空間的上下文切換(第二次上下文切換)。DMA gather copy根據(jù)socket緩沖區(qū)中描述符提供的位置和偏移量信息直接將內(nèi)核空間緩沖區(qū)中的數(shù)據(jù)拷貝到協(xié)議引擎上(第二次拷貝: kernel buffer ——> protocol engine)歌懒,這樣就避免了最后一次CPU數(shù)據(jù)拷貝啦桌。

  • 4、帶有DMA收集拷貝功能的sendfile實(shí)現(xiàn)的I/O只使用了2次用戶空間與內(nèi)核空間的上下文切換及皂,以及2次數(shù)據(jù)的拷貝甫男,而且這2次的數(shù)據(jù)拷貝都是非CPU拷貝。這樣一來我們就實(shí)現(xiàn)了最理想的零拷貝I/O傳輸了验烧,不需要任何一次的CPU拷貝查剖,以及最少的上下文切換。

許多Web服務(wù)器都支持零拷貝噪窘,如Tomcat和Apache笋庄。 例如Apache的相關(guān)文檔可以在這里找到效扫,但默認(rèn)情況下關(guān)閉。
注意:Java的NIO通過transferTo()提供了這個(gè)功能直砂。

  • 傳統(tǒng)I/O用戶空間緩沖區(qū)中存有數(shù)據(jù)菌仁,因此應(yīng)用程序能夠?qū)Υ藬?shù)據(jù)進(jìn)行修改等操作;而sendfile零拷貝消除了所有內(nèi)核空間緩沖區(qū)與用戶空間緩沖區(qū)之間的數(shù)據(jù)拷貝過程静暂,因此sendfile零拷貝I/O的實(shí)現(xiàn)是完成在內(nèi)核空間中完成的济丘,這對于應(yīng)用程序來說就無法對數(shù)據(jù)進(jìn)行操作了。為了解決這個(gè)問題洽蛀,Linux提供了mmap零拷貝來實(shí)現(xiàn)我們的需求摹迷。

7 通過mmap實(shí)現(xiàn)的零拷貝I/O

  • mmap(內(nèi)存映射)是一個(gè)比sendfile昂貴但優(yōu)于傳統(tǒng)I/O的方法。


    mmap
  • 1郊供、發(fā)出mmap系統(tǒng)調(diào)用峡碉,導(dǎo)致用戶空間到內(nèi)核空間的上下文切換(第一次上下文切換)。通過DMA引擎將磁盤文件中的內(nèi)容拷貝到內(nèi)核空間緩沖區(qū)中(第一次拷貝: hard drive ——> kernel buffer)驮审。

  • 2鲫寄、mmap系統(tǒng)調(diào)用返回,導(dǎo)致內(nèi)核空間到用戶空間的上下文切換(第二次上下文切換)疯淫。接著用戶空間和內(nèi)核空間共享這個(gè)緩沖區(qū)地来,而不需要將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間。因?yàn)橛脩艨臻g和內(nèi)核空間共享了這個(gè)緩沖區(qū)數(shù)據(jù)熙掺,所以用戶空間就可以像在操作自己緩沖區(qū)中數(shù)據(jù)一般操作這個(gè)由內(nèi)核空間共享的緩沖區(qū)數(shù)據(jù)未斑。

  • 3、發(fā)出write系統(tǒng)調(diào)用币绩,導(dǎo)致用戶空間到內(nèi)核空間的上下文切換(第三次上下文切換)颂碧。將數(shù)據(jù)從內(nèi)核空間緩沖區(qū)拷貝到內(nèi)核空間socket相關(guān)聯(lián)的緩沖區(qū)(第二次拷貝: kernel buffer ——> socket buffer)。

  • 4类浪、write系統(tǒng)調(diào)用返回载城,導(dǎo)致內(nèi)核空間到用戶空間的上下文切換(第四次上下文切換)。通過DMA引擎將內(nèi)核空間socket緩沖區(qū)中的數(shù)據(jù)傳遞到協(xié)議引擎(第三次拷貝: socket buffer ——> protocol engine)

通過mmap實(shí)現(xiàn)的零拷貝I/O進(jìn)行了4次用戶空間與內(nèi)核空間的上下文切換费就,以及3次數(shù)據(jù)拷貝诉瓦。其中3次數(shù)據(jù)拷貝中包括了2次DMA拷貝和1次CPU拷貝。明顯力细,它與傳統(tǒng)I/O相比僅僅少了1次內(nèi)核空間緩沖區(qū)和用戶空間緩沖區(qū)之間的CPU拷貝睬澡。這樣的好處是,我們可以將整個(gè)文件或者整個(gè)文件的一部分映射到內(nèi)存當(dāng)中眠蚂,用戶直接對內(nèi)存中對文件進(jìn)行操作煞聪,然后是由操作系統(tǒng)來進(jìn)行相關(guān)的頁面請求并將內(nèi)存的修改寫入到文件當(dāng)中。我們的應(yīng)用程序只需要處理內(nèi)存的數(shù)據(jù)逝慧,這樣可以實(shí)現(xiàn)非常迅速的I/O操作昔脯。

8啄糙、NIO DirectByteBuffer

  • Java NIO引入了用于通道的緩沖區(qū)的ByteBuffer。 ByteBuffer有三個(gè)主要的實(shí)現(xiàn):HeapByteBuffer云稚、DirectByteBuffer隧饼、MappedByteBuffer
類名 類描述說明
HeapByteBuffer 堆緩沖區(qū)
DirectByteBuffer 直接緩沖區(qū)
MappedByteBuffer 映射緩沖區(qū)

HeapByteBuffer

  • 在調(diào)用ByteBuffer.allocate()時(shí)使用。 它被稱為堆静陈,因?yàn)樗4嬖贘VM的堆空間中燕雁,因此你可以獲得所有優(yōu)勢,如GC支持和緩存優(yōu)化鲸拥。 但是拐格,它不是頁面對齊的,這意味著如果你需要通過JNI與本地代碼交談刑赶,JVM將不得不復(fù)制到對齊的緩沖區(qū)空間捏浊。


    HeapByteBuffer

DirectByteBuffer

  • 在調(diào)用ByteBuffer.allocateDirect()時(shí)使用。 JVM將使用malloc()在堆空間之外分配內(nèi)存空間角撞。 因?yàn)樗皇怯蒍VM管理的呛伴,所以你的內(nèi)存空間是頁面對齊的勃痴,不受GC影響谒所,這使得它成為處理本地代碼的完美選擇。 然而沛申,你要C程序員一樣劣领,自己管理這個(gè)內(nèi)存,必須自己分配和釋放內(nèi)存來防止內(nèi)存泄漏铁材。


    DirectByteBuffer

MappedByteBuffer

  • 在調(diào)用FileChannel.map()時(shí)使用尖淘。 與DirectByteBuffer類似,這也是JVM堆外部的情況著觉。 它基本上作為OS mmap()系統(tǒng)調(diào)用的包裝函數(shù)村生,以便代碼直接操作映射的物理內(nèi)存數(shù)據(jù)


    MappedByteBuffer

9 總結(jié)

  • 零拷貝是操作系統(tǒng)底層的一種實(shí)現(xiàn),我們在網(wǎng)絡(luò)編程中饼丘,利用操作系統(tǒng)這一特性趁桃,可以大大提高數(shù)據(jù)傳輸?shù)男省_@也是目前網(wǎng)絡(luò)編程框架中都會(huì)采用的方式肄鸽,理解好零拷貝卫病,有助于我們進(jìn)一步學(xué)習(xí)Netty等網(wǎng)絡(luò)通信框架的底層原理。
  • 以上文章來自 Java NIO學(xué)習(xí)筆記四(零拷貝詳解)進(jìn)行個(gè)人整理
    推薦看堆外內(nèi)存典徘、零拷貝蟀苛、DirectByteBuffer
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市逮诲,隨后出現(xiàn)的幾起案子帜平,更是在濱河造成了極大的恐慌幽告,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罕模,死亡現(xiàn)場離奇詭異评腺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)淑掌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門蒿讥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抛腕,你說我怎么就攤上這事芋绸。” “怎么了担敌?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵摔敛,是天一觀的道長。 經(jīng)常有香客問我全封,道長马昙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任刹悴,我火速辦了婚禮行楞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘土匀。我一直安慰自己子房,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布就轧。 她就那樣靜靜地躺著证杭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妒御。 梳的紋絲不亂的頭發(fā)上解愤,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機(jī)與錄音乎莉,去河邊找鬼送讲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛梦鉴,可吹牛的內(nèi)容都是我干的李茫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肥橙,長吁一口氣:“原來是場噩夢啊……” “哼魄宏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起存筏,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤宠互,失蹤者是張志新(化名)和其女友劉穎味榛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體予跌,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搏色,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了券册。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片频轿。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酝静,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布膳殷,位于F島的核電站,受9級特大地震影響九火,放射性物質(zhì)發(fā)生泄漏赚窃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一岔激、第九天 我趴在偏房一處隱蔽的房頂上張望勒极。 院中可真熱鬧,春花似錦鹦倚、人聲如沸河质。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至散休,卻和暖如春媒楼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背戚丸。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工划址, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人限府。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓夺颤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胁勺。 傳聞我的和親對象是個(gè)殘疾皇子世澜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容