Zero-Copy的原理簡介

單獨(dú)從理論上理解可能會(huì)有些晦澀究飞,我在這從一個(gè)實(shí)際的場景進(jìn)行闡述舌劳,一步一步演進(jìn)Zero-Copy的原理。


概述

我們的系統(tǒng)哮针,不管是電商系統(tǒng)還是官網(wǎng)都是一種交互式請(qǐng)求響應(yīng)模式,不過今天不是講http的請(qǐng)求/響應(yīng)模式坦袍。打開一個(gè)頁面十厢,對(duì)于系統(tǒng)而言需要將系統(tǒng)生成的view靜態(tài)內(nèi)容(類似圖片、文件)展示給用戶捂齐,以此場景切入蛮放,系統(tǒng)需要先將靜態(tài)內(nèi)容從磁盤中拷貝出來放到一個(gè)內(nèi)存buf中,然后將這個(gè)buf通過socket傳輸給用戶奠宜,進(jìn)而用戶或者靜態(tài)內(nèi)容的展示包颁。這看起來再正常不過了瞻想,但是實(shí)際上這是很低效的流程,我們把上面的這種情形抽象成下面的過程:

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

首先調(diào)用read將靜態(tài)內(nèi)容娩嚼,這里假設(shè)為文件File蘑险,讀取到tmp_buf, 然后調(diào)用write將tmp_buf寫入到socket中,如圖:

image.png

在這個(gè)過程中文件File的經(jīng)歷了4次copy的過程:
首先岳悟,調(diào)用read時(shí)佃迄,文件File拷貝到了kernel模式;
之后贵少,CPU控制將kernel模式數(shù)據(jù)copy到user模式下呵俏;
調(diào)用write時(shí),先將user模式下的內(nèi)容copy到kernel模式下的socket的buffer中春瞬;
最后將kernel模式下的socket buffer的數(shù)據(jù)copy到網(wǎng)卡設(shè)備中傳送;
從上面的過程可以看出套啤,數(shù)據(jù)白白從kernel模式到user模式走了一圈宽气,浪費(fèi)了2次copy(第一次,從kernel模式拷貝到user模式潜沦;第二次從user模式再拷貝回kernel模式萄涯,即上面4次過程的第2和3步驟。)唆鸡。而且上面的過程中kernel和user模式的上下文的切換也是4次涝影。
幸運(yùn)的是,你可以用一種叫做Zero-Copy的技術(shù)來去掉這些無謂的copy争占。應(yīng)用程序用Zero-Copy來請(qǐng)求kernel直接把disk的data傳輸給socket燃逻,而不是通過應(yīng)用程序傳輸。Zero-Copy大大提高了應(yīng)用程序的性能臂痕,并且減少了kernel和user模式上下文的切換伯襟。

javaNIO 改進(jìn)

Zero-Copy技術(shù)省去了將操作系統(tǒng)的read buffer拷貝到程序的buffer,以及從程序buffer拷貝到socket buffer的步驟握童,直接將read buffer拷貝到socket buffer. Java NIO中的FileChannal.transferTo()方法就是這樣的實(shí)現(xiàn)姆怪,這個(gè)實(shí)現(xiàn)是依賴于操作系統(tǒng)底層的sendFile()實(shí)現(xiàn)的。

public abstract long transferTo(long position, long count,
                                    WritableByteChannel target)

底層調(diào)用sendfile方法

#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

下圖為transferTo()之后的數(shù)據(jù)流向:


image.png

下圖展示了在使用transferTo()之后的上下文切換:


image.png

使用了Zero-Copy技術(shù)之后澡绩,整個(gè)過程如下:
transferTo()方法使得文件A的內(nèi)容直接拷貝到一個(gè)read buffer(kernel buffer)中稽揭;

然后數(shù)據(jù)(kernel buffer)拷貝到socket buffer中。
最后將socket buffer中的數(shù)據(jù)拷貝到網(wǎng)卡設(shè)備(protocol engine)中傳輸肥卡;
這顯然是一個(gè)偉大的進(jìn)步:這里把上下文的切換次數(shù)從4次減少到2次溪掀,同時(shí)也把數(shù)據(jù)copy的次數(shù)從4次降低到了3次。
但是這是Zero-Copy么步鉴,答案是否定的膨桥。

真正的Zero-Copy進(jìn)階

Linux 2.1內(nèi)核開始引入了sendfile函數(shù)(上一節(jié)有提到),用于將文件通過socket傳送蛮浑。

sendfile(socket, file, len);

該函數(shù)通過一次系統(tǒng)調(diào)用完成了文件的傳送,減少了原來read/write方式的模式切換只嚣。此外更是減少了數(shù)據(jù)的copy, sendfile的詳細(xì)過程如圖:


image.png

通過sendfile傳送文件只需要一次系統(tǒng)調(diào)用沮稚,當(dāng)調(diào)用sendfile時(shí):
首先(通過DMA)將數(shù)據(jù)從磁盤讀取到kernel buffer中;
然后將kernel buffer拷貝到socket buffer中册舞;
最后將socket buffer中的數(shù)據(jù)copy到網(wǎng)卡設(shè)備(protocol engine)中發(fā)送蕴掏;
這個(gè)過程就是第二節(jié)(詳述)中的那個(gè)步驟。
sendfile與read/write模式相比调鲸,少了一次copy盛杰。但是從上述過程中也可以發(fā)現(xiàn)從kernel buffer中將數(shù)據(jù)copy到socket buffer是沒有必要的。
Linux2.4 內(nèi)核對(duì)sendfile做了改進(jìn)藐石,如圖:


image.png

改進(jìn)后的處理過程如下:
將文件拷貝到kernel buffer中即供;

向socket buffer中追加當(dāng)前要發(fā)生的數(shù)據(jù)在kernel buffer中的位置和偏移量;
根據(jù)socket buffer中的位置和偏移量直接將kernel buffer的數(shù)據(jù)copy到網(wǎng)卡設(shè)備(protocol engine)中于微;
經(jīng)過上述過程逗嫡,數(shù)據(jù)只經(jīng)過了2次copy就從磁盤傳送出去了。這個(gè)才是真正的Zero-Copy(這里的零拷貝是針對(duì)kernel來講的株依,數(shù)據(jù)在kernel模式下是Zero-Copy)驱证。
正是Linux2.4的內(nèi)核做了改進(jìn),Java中的TransferTo()實(shí)現(xiàn)了Zero-Copy,如下圖:


image.png

Zero-Copy技術(shù)的使用場景有很多恋腕,比如Kafka, 又或者是Netty等抹锄,可以大大提升程序的性能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末荠藤,一起剝皮案震驚了整個(gè)濱河市伙单,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哈肖,老刑警劉巖车份,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異牡彻,居然都是意外死亡扫沼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門庄吼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缎除,“玉大人,你說我怎么就攤上這事总寻∑鞴蓿” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵渐行,是天一觀的道長轰坊。 經(jīng)常有香客問我铸董,道長,這世上最難降的妖魔是什么肴沫? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任粟害,我火速辦了婚禮,結(jié)果婚禮上颤芬,老公的妹妹穿的比我還像新娘悲幅。我一直安慰自己,他們只是感情好站蝠,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布汰具。 她就那樣靜靜地躺著,像睡著了一般菱魔。 火紅的嫁衣襯著肌膚如雪留荔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天澜倦,我揣著相機(jī)與錄音聚蝶,去河邊找鬼。 笑死肥隆,一個(gè)胖子當(dāng)著我的面吹牛既荚,可吹牛的內(nèi)容都是我干的稚失。 我是一名探鬼主播栋艳,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼句各!你這毒婦竟也來了吸占?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤凿宾,失蹤者是張志新(化名)和其女友劉穎矾屯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體初厚,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡件蚕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了产禾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片排作。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖亚情,靈堂內(nèi)的尸體忽然破棺而出妄痪,到底是詐尸還是另有隱情,我是刑警寧澤楞件,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布衫生,位于F島的核電站裳瘪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏罪针。R本人自食惡果不足惜彭羹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望站故。 院中可真熱鬧皆怕,春花似錦、人聲如沸西篓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岂津。三九已至虱黄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吮成,已是汗流浹背橱乱。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粱甫,地道東北人泳叠。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像茶宵,于是被迫代替她去往敵國和親危纫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348