我試著來(lái)講明白零拷貝|面試怪圈(著)

談?wù)劚尘?/h2>

第一次接觸零拷貝,噼里啪啦各種雜談概念內(nèi)核摆碉、上下文切換巷帝、DMA扫夜、MMAP....看了不少文章,不知道你是否也覺(jué)得是云里霧里棍厂,纏繞不清。也許把一件事情說(shuō)清楚勋桶,首先要貼近程序員能夠感觸到的“0距離”的場(chǎng)景侥猬。

也許你覺(jué)得零拷貝是面試大綱中常見(jiàn)的一綱退唠,毫無(wú)用處瞧预。但你每天確實(shí)都在接觸它,你卻并未發(fā)現(xiàn)它盆驹。比如:rocketMQ一死、Kafka的消費(fèi)者。

你細(xì)品梧兼,為什么這兩者會(huì)涉及到零拷貝呢?拷貝——Ctrl+C再熟悉不過(guò)的騷操作焦履。

消費(fèi)者發(fā)起消費(fèi)的過(guò)程是這樣的:將數(shù)據(jù)從磁盤(pán)讀取出來(lái)裁良,通過(guò)網(wǎng)絡(luò)傳輸傳遞給消費(fèi)者校套。而這其中將數(shù)據(jù)從磁盤(pán)到網(wǎng)卡的過(guò)程笛匙,就是數(shù)據(jù)拷貝。數(shù)據(jù)移動(dòng)肯定需要資源消耗秋柄,比如CPU骇笔、上下文切換等。然而簡(jiǎn)簡(jiǎn)單單的數(shù)據(jù)拷貝的過(guò)程懦傍,內(nèi)部的數(shù)據(jù)流動(dòng)并不簡(jiǎn)單粗俱。因此看了下面的介紹寸认,你一定會(huì)明白為什么要零拷貝串慰?

傳統(tǒng)的IO拷貝

舉例

以下是消費(fèi)者消費(fèi)數(shù)據(jù)為例邦鲫,為了模擬數(shù)據(jù)從磁盤(pán)到網(wǎng)卡的過(guò)程,我借用一段代碼,讓Java同學(xué)能感受到我們?cè)谧鍪裁刺墼铮_切的說(shuō)醉者,我們是在解釋為什么傳統(tǒng)IO不是很理想:

// 模擬讀取topic_data.db這個(gè)數(shù)據(jù)文件
File file = new File("D://topic_data.db");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
byte[] arr = new byte[(int) file.length()];
raf.read(arr);

// 將讀取的字節(jié)碼通過(guò)socket傳輸出去
Socket socket = new ServerSocket(8080).accept();
socket.getOutputStream().write(arr);

圖文說(shuō)明

以下是我結(jié)合網(wǎng)上資料撬即,手繪了一張傳統(tǒng)IO讀寫(xiě)的流程圖呈队,來(lái)解釋上述代碼的執(zhí)行流程:

image.png

內(nèi)核空間與用戶(hù)空間:為了保證內(nèi)核的安全宪摧,現(xiàn)在的操作系統(tǒng)一般都強(qiáng)制用戶(hù)進(jìn)程不能直接操作內(nèi)核。具體的實(shí)現(xiàn)方式基本都是由操作系統(tǒng)將虛擬地址空間劃分為兩部分沿后,一部分為內(nèi)核空間尖滚,另一部分為用戶(hù)空間瞧柔。

讀流程和寫(xiě)流程大致是這樣的非剃,如果不是特別好理解,建議強(qiáng)迫記憶券坞,因?yàn)檫@是以下能夠繼續(xù)探究的基礎(chǔ):

  1. 應(yīng)用程序調(diào)用內(nèi)核指令讀取文件恨锚。
  2. 文件通過(guò)DMA控制器拷貝到內(nèi)核緩沖區(qū)(ReadBuffer
  3. cpu將內(nèi)核緩存沖區(qū)的數(shù)據(jù)拷貝到應(yīng)用程序緩沖區(qū)猴伶。
  4. cpu將應(yīng)用程序緩沖區(qū)的數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)(SocketBuffer
  5. 通過(guò)DMA控制器將數(shù)據(jù)拷貝至網(wǎng)卡
  6. 拷貝完成后他挎,通知應(yīng)用程序捡需。

那么站辉,DMA又是個(gè)啥?DMA這東東翻譯過(guò)來(lái)叫直接內(nèi)存訪(fǎng)問(wèn),顧名思義殊霞,直接訪(fǎng)問(wèn)到內(nèi)存绷蹲。你想想瘸右,將數(shù)據(jù)從一塊區(qū)域拷貝到另外一塊區(qū)域,cpu肯定得負(fù)責(zé)搬運(yùn)苞俘。而這個(gè)DMA的誕生讓cpu盡量不參與搬運(yùn)吃谣,更多的時(shí)間去處理其他的事情做裙。你可以參考正規(guī)的解釋?zhuān)?/p>

Direct Memory Access(存儲(chǔ)器直接訪(fǎng)問(wèn))锚贱。這是指一種高速的數(shù)據(jù)傳輸操作拧廊,允許在外部設(shè)備和存儲(chǔ)器之間直接讀寫(xiě)數(shù)據(jù)。整個(gè)數(shù)據(jù)傳輸操作在一個(gè)稱(chēng)為"DMA控制器"的控制下進(jìn)行的凰盔。CPU除了在數(shù)據(jù)傳輸開(kāi)始和結(jié)束時(shí)做一點(diǎn)處理外(開(kāi)始和結(jié)束時(shí)候要做中斷處理)户敬,在傳輸過(guò)程中CPU可以進(jìn)行其他的工作(前提是未設(shè)置停止CPU訪(fǎng)問(wèn))尿庐。這樣屁倔,在大部分時(shí)間里暮胧,CPU和輸入輸出都處于并行操作往衷。因此席舍,使整個(gè)計(jì)算機(jī)系統(tǒng)的效率大大提高哮笆。

探究細(xì)節(jié)

簡(jiǎn)單的流程梳理,我們對(duì)一些細(xì)節(jié)做下統(tǒng)計(jì):

  • 上下文切換次數(shù)(圖1中的粉色圓圈):4次
  • 數(shù)據(jù)拷貝次數(shù)(圖1中的綠色圓圈):4次
  • cpu參與次數(shù):2次

很明顯萝毛,每次操作都需要內(nèi)核及硬件的成本付出笆包,如何減少對(duì)應(yīng)的次數(shù)就是零拷貝真正要解決的問(wèn)題略荡。

上下文切換

為什么用戶(hù)空間切換到內(nèi)核空間開(kāi)銷(xiāo)比較大汛兜?甚至有人叫這破玩意叫上下文切換?我摘抄一段文字粥谬。你可以磨一磨帝嗡、品一品:

當(dāng)程序中有系統(tǒng)調(diào)用語(yǔ)句,程序執(zhí)行到系統(tǒng)調(diào)用時(shí)狮辽,首先使用類(lèi)似int 80H的軟中斷指令喉脖,保存現(xiàn)場(chǎng)树叽,去系統(tǒng)調(diào)用谦絮,在內(nèi)核態(tài)執(zhí)行层皱,然后恢復(fù)現(xiàn)場(chǎng)叫胖,每個(gè)進(jìn)程都會(huì)有兩個(gè)棧,一個(gè)內(nèi)核態(tài)棧和一個(gè)用戶(hù)態(tài)棧怎棱。當(dāng)int中斷執(zhí)行時(shí)就會(huì)由用戶(hù)態(tài)棧轉(zhuǎn)向內(nèi)核態(tài)棧拳恋。系統(tǒng)調(diào)用時(shí)需要進(jìn)行棧的切換诅岩。而且內(nèi)核代碼對(duì)用戶(hù)不信任吩谦,需要進(jìn)行額外的檢查。系統(tǒng)調(diào)用的返回過(guò)程有很多額外工作咐扭,比如檢查是否需要調(diào)度等蝗肪。

系統(tǒng)調(diào)用一般都需要保存用戶(hù)程序的上下文(context), 在進(jìn)入內(nèi)核的時(shí)候需要保存用戶(hù)態(tài)的寄存器薛闪,在內(nèi)核態(tài)返回用戶(hù)態(tài)的時(shí)候會(huì)恢復(fù)這些寄存器的內(nèi)容豁延。這是一個(gè)開(kāi)銷(xiāo)的地方诱咏。 如果需要在不同用戶(hù)程序間切換的話(huà)缴挖,那么還要更新cr3寄存器映屋,這樣會(huì)更換每個(gè)程序的虛擬內(nèi)存到物理內(nèi)存映射表的地址棚点,也是一個(gè)比較高負(fù)擔(dān)的操作乙濒。

再談零拷貝

講了這么多傳統(tǒng)IO,目的是為了理解零拷貝做鋪墊么库,零拷貝是基于傳統(tǒng)IO的改進(jìn)版诉儒。

在開(kāi)始之前忱反,我們先看看什么是虛擬內(nèi)存地址:

虛擬內(nèi)存地址

所有現(xiàn)代操作系統(tǒng)都使用虛擬內(nèi)存温算,使用虛擬地址取代物理地址注竿,這樣做的好處就是:

1魂贬、多個(gè)虛擬內(nèi)存可以指向同一個(gè)物理地址
2付燥、虛擬內(nèi)存空間可以遠(yuǎn)遠(yuǎn)大于物理內(nèi)存空間

如果把圖1內(nèi)核空間用戶(hù)空間的虛擬地址映射到同一個(gè)物理地址键科,就不需要cpu將數(shù)據(jù)在內(nèi)核空間用戶(hù)空間來(lái)回拷貝萝嘁。

image.png

mmap+write與sendfile

mmap+write就是利用虛擬內(nèi)存地址的方式牙言,減少內(nèi)核空間和用戶(hù)空間的數(shù)據(jù)拷貝咱枉,從而減少數(shù)據(jù)拷貝次數(shù)蚕断。我們看下mmap+write的讀寫(xiě)流程:

image.png

從上圖可以看出亿乳,mmap與傳統(tǒng)IO讀流程的區(qū)別只是在內(nèi)核空間與用戶(hù)空間數(shù)據(jù)采用的虛擬內(nèi)存地址的方式共享內(nèi)存,減少了一次Cpu的數(shù)據(jù)拷貝滋恬,然而恢氯,上下文切換次數(shù)并未減少勋拟。

write()流程如下:

image.png

由于應(yīng)用程序緩沖區(qū)與內(nèi)核緩沖區(qū)共享內(nèi)存敢靡,cpu只需要將ReadBuffer數(shù)據(jù)拷貝到SocketBuffer醋安。

那么吓揪,來(lái)綜合看下mmap+write的方式成本消耗如何柠辞?

  • 上下文切換次數(shù):4次
  • 數(shù)據(jù)拷貝次數(shù):3次
  • cpu參與次數(shù):1次

mmap+write相對(duì)傳統(tǒng)Io叭首,減少了一次cpu的數(shù)據(jù)拷貝焙格,然而上下文切換次數(shù)并沒(méi)有減少夷都,你試想一下囤官,如果應(yīng)用程序與內(nèi)核只做一次交互不就可以減少2次上下文切換党饮,因此刑顺,sendfile()相對(duì)mmap()+write()就是做了這一點(diǎn)的結(jié)合性改善。參考下圖(盜圖一張不皆,不留名,嘿嘿):

image.png

寫(xiě)在最后

說(shuō)了這么多傳統(tǒng)IO鲫骗、mmap以及sendfile执泰,我們來(lái)做下比對(duì):

  • 傳統(tǒng) IO 執(zhí)行的話(huà)需要 4 次上下文切換(用戶(hù)態(tài) -> 內(nèi)核態(tài) -> 用戶(hù)態(tài) -> 內(nèi)核態(tài) -> 用戶(hù)態(tài))和 4 次拷貝(磁盤(pán)文件 DMA 拷貝到內(nèi)核緩沖區(qū)术吝,內(nèi)核緩沖區(qū) CPU 拷貝到用戶(hù)緩沖區(qū)排苍,用戶(hù)緩沖區(qū) CPU 拷貝到 Socket 緩沖區(qū)淘衙,Socket 緩沖區(qū) DMA 拷貝到協(xié)議引擎)彤守。

  • mmap 將磁盤(pán)文件映射到內(nèi)存具垫,支持讀和寫(xiě)筝蚕,對(duì)內(nèi)存的操作會(huì)反映在磁盤(pán)文件上饰及,適合小數(shù)據(jù)量讀寫(xiě)康震,需要 4 次上下文切換(用戶(hù)態(tài) -> 內(nèi)核態(tài) -> 用戶(hù)態(tài) -> 內(nèi)核態(tài) -> 用戶(hù)態(tài))和3 次拷貝(磁盤(pán)文件DMA拷貝到內(nèi)核緩沖區(qū),內(nèi)核緩沖區(qū) CPU 拷貝到 Socket 緩沖區(qū)腿短,Socket 緩沖區(qū) DMA 拷貝到協(xié)議引擎)屏箍。

  • sendfile 是將讀到內(nèi)核空間的數(shù)據(jù)绘梦,轉(zhuǎn)到 socket buffer,進(jìn)行網(wǎng)絡(luò)發(fā)送赴魁,適合大文件傳輸卸奉,只需要 2 次上下文切換(用戶(hù)態(tài) -> 內(nèi)核態(tài) -> 用戶(hù)態(tài))和 2 次拷貝(磁盤(pán)文件 DMA 拷貝到內(nèi)核緩沖區(qū),內(nèi)核緩沖區(qū) DMA 拷貝到協(xié)議引擎)颖御。

此外,零拷貝其實(shí)也沒(méi)有真正意義上的清零潘拱,只是相對(duì)傳統(tǒng)IO進(jìn)行了性能優(yōu)化:

  • 1.采用虛擬內(nèi)存地址的方式共享內(nèi)存疹鳄,減少內(nèi)核與用戶(hù)空間數(shù)據(jù)拷貝的次數(shù)。
  • 2.拷貝次數(shù)的減少芦岂,間接減少了cpu的參與次數(shù)瘪弓。
  • 3.sendfile這種方式減少了上下文切換的次數(shù)。
  • 4.同時(shí)禽最,DMA控制也是一種減少cpu參與數(shù)據(jù)拷貝的方式腺怯。

因此,減少數(shù)據(jù)拷貝川无、CPU參與呛占、上下文切換才是零拷貝最具靈魂、最絕的一筆懦趋!

作者介紹

keaizhuzhu栓票,公眾號(hào)面試怪圈小編,網(wǎng)站面試怪圈站長(zhǎng)愕够,曾就職于阿里巴巴本地生活走贪,目前就職于京東做后端開(kāi)發(fā)。

編寫(xiě)過(guò)《Java面試怪圈內(nèi)卷手冊(cè)》面試秘籍惑芭,全網(wǎng)閱讀量過(guò)萬(wàn)次坠狡。

官網(wǎng):http://www.msgqer.com。旨在分享前端遂跟、后端逃沿、大數(shù)據(jù)、各種中間件技術(shù)的面試資料幻锁,總訪(fǎng)問(wèn)量數(shù)萬(wàn)次凯亮。點(diǎn)擊【閱讀原文】可直達(dá)。

Java后端在線(xiàn)面試題地址:http://www.msgqer.com/case/fwCase

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哄尔,一起剝皮案震驚了整個(gè)濱河市假消,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌岭接,老刑警劉巖富拗,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件臼予,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡啃沪,警方通過(guò)查閱死者的電腦和手機(jī)粘拾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)创千,“玉大人缰雇,你說(shuō)我怎么就攤上這事∽仿浚” “怎么了寓涨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)氯檐。 經(jīng)常有香客問(wèn)我,道長(zhǎng)体捏,這世上最難降的妖魔是什么冠摄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮几缭,結(jié)果婚禮上河泳,老公的妹妹穿的比我還像新娘。我一直安慰自己年栓,他們只是感情好拆挥,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著某抓,像睡著了一般纸兔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上否副,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天汉矿,我揣著相機(jī)與錄音,去河邊找鬼备禀。 笑死洲拇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的曲尸。 我是一名探鬼主播赋续,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼另患!你這毒婦竟也來(lái)了纽乱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤昆箕,失蹤者是張志新(化名)和其女友劉穎迫淹,沒(méi)想到半個(gè)月后秘通,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敛熬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年肺稀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片应民。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡话原,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诲锹,到底是詐尸還是另有隱情繁仁,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布归园,位于F島的核電站黄虱,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏庸诱。R本人自食惡果不足惜捻浦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望桥爽。 院中可真熱鬧朱灿,春花似錦、人聲如沸钠四。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)缀去。三九已至侣灶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缕碎,已是汗流浹背炫隶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阎曹,地道東北人伪阶。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像处嫌,于是被迫代替她去往敵國(guó)和親栅贴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

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