深入理解零拷貝技術(shù)


File.read(file, buf, len);
Socket.send(socket, buf, len);

CPU 負(fù)責(zé)將數(shù)據(jù)從磁盤搬運(yùn)到內(nèi)核空間的 Page Cache 中;
CPU 負(fù)責(zé)將數(shù)據(jù)從內(nèi)核空間的 Socket 緩沖區(qū)搬運(yùn)到的網(wǎng)絡(luò)中;
CPU 負(fù)責(zé)將數(shù)據(jù)從內(nèi)核空間的 Page Cache 搬運(yùn)到用戶空間的緩沖區(qū);
CPU 負(fù)責(zé)將數(shù)據(jù)從用戶空間的緩沖區(qū)搬運(yùn)到內(nèi)核空間的 Socket 緩沖區(qū)中。
read 系統(tǒng)調(diào)用時(shí):用戶態(tài)切換到內(nèi)核態(tài);
read 系統(tǒng)調(diào)用完畢:內(nèi)核態(tài)切換回用戶態(tài);
write 系統(tǒng)調(diào)用時(shí):用戶態(tài)切換到內(nèi)核態(tài);
write 系統(tǒng)調(diào)用完畢:內(nèi)核態(tài)切換回用戶態(tài)。
CPU 全程負(fù)責(zé)內(nèi)存內(nèi)的數(shù)據(jù)拷貝還可以接受,因?yàn)樾蔬€算可以接受,但是如果要全程負(fù)責(zé)內(nèi)存與磁盤、網(wǎng)絡(luò)的數(shù)據(jù)拷貝,這將難以接受,因?yàn)榇疟P、網(wǎng)卡的速度遠(yuǎn)小于內(nèi)存,內(nèi)存又遠(yuǎn)遠(yuǎn)小于 CPU;
4 次 copy 太多了,4 次上下文切換也太頻繁了。





sendfile
mmap
splice
直接 Direct I/O
DMA 技術(shù)回顧:DMA 負(fù)責(zé)內(nèi)存與其他組件之間的數(shù)據(jù)拷貝,CPU 僅需負(fù)責(zé)管理,而無(wú)需負(fù)責(zé)全程的數(shù)據(jù)拷貝;
使用 page cache 的 zero copy:
sendfile:一次代替 read/write 系統(tǒng)調(diào)用,通過(guò)使用 DMA 技術(shù)以及傳遞文件描述符,實(shí)現(xiàn)了 zero copy
mmap:僅代替 read 系統(tǒng)調(diào)用,將內(nèi)核空間地址映射為用戶空間地址,write 操作直接作用于內(nèi)核空間。通過(guò) DMA 技術(shù)以及地址映射技術(shù),用戶空間與內(nèi)核空間無(wú)須數(shù)據(jù)拷貝,實(shí)現(xiàn)了 zero copy
不使用 page cache 的 Direct I/O:讀寫操作直接在磁盤上進(jìn)行,不使用 page cache 機(jī)制,通常結(jié)合用戶空間的用戶緩存使用。通過(guò) DMA 技術(shù)直接與磁盤/網(wǎng)卡進(jìn)行數(shù)據(jù)交互,實(shí)現(xiàn)了 zero copy
DMA 技術(shù);
傳遞文件描述符代替數(shù)據(jù)拷貝。

page cache 以及 socket buffer 都在內(nèi)核空間中;
數(shù)據(jù)傳輸過(guò)程前后沒(méi)有任何寫操作。


緩存文件 I/O:用戶空間要讀寫一個(gè)文件并不直接與磁盤交互,而是中間夾了一層緩存,即 page cache;
直接文件 I/O:用戶空間讀取的文件直接與磁盤交互,沒(méi)有中間 page cache 層。

Write 操作:由于其不使用 page cache,所以其進(jìn)行寫文件,如果返回成功,數(shù)據(jù)就真的落盤了(不考慮磁盤自帶的緩存);
Read 操作:由于其不使用 page cache,每次讀操作是真的從磁盤中讀取,不會(huì)從文件系統(tǒng)的緩存中讀取。
Linux 中的直接 I/O 技術(shù)省略掉緩存 I/O 技術(shù)中操作系統(tǒng)內(nèi)核緩沖區(qū)的使用,數(shù)據(jù)直接在應(yīng)用程序地址空間和磁盤之間進(jìn)行傳輸,從而使得自緩存應(yīng)用程序可以省略掉復(fù)雜的系統(tǒng)級(jí)別的緩存結(jié)構(gòu),而執(zhí)行程序自己定義的數(shù)據(jù)讀寫管理,從而降低系統(tǒng)級(jí)別的管理對(duì)應(yīng)用程序訪問(wèn)數(shù)據(jù)的影響。
與其他零拷貝技術(shù)一樣,避免了內(nèi)核空間到用戶空間的數(shù)據(jù)拷貝,如果要傳輸?shù)臄?shù)據(jù)量很大,使用直接 I/O 的方式進(jìn)行數(shù)據(jù)傳輸,而不需要操作系統(tǒng)內(nèi)核地址空間拷貝數(shù)據(jù)操作的參與,這將會(huì)大大提高性能。
由于設(shè)備之間的數(shù)據(jù)傳輸是通過(guò) DMA 完成的,因此用戶空間的數(shù)據(jù)緩沖區(qū)內(nèi)存頁(yè)必須進(jìn)行 page pinning(頁(yè)鎖定),這是為了防止其物理頁(yè)框地址被交換到磁盤或者被移動(dòng)到新的地址而導(dǎo)致 DMA 去拷貝數(shù)據(jù)的時(shí)候在指定的地址找不到內(nèi)存頁(yè)從而引發(fā)缺頁(yè)錯(cuò)誤,而頁(yè)鎖定的開銷并不比 CPU 拷貝小,所以為了避免頻繁的頁(yè)鎖定系統(tǒng)調(diào)用,應(yīng)用程序必須分配和注冊(cè)一個(gè)持久的內(nèi)存池,用于數(shù)據(jù)緩沖。
如果訪問(wèn)的數(shù)據(jù)不在應(yīng)用程序緩存中,那么每次數(shù)據(jù)都會(huì)直接從磁盤進(jìn)行加載,這種直接加載會(huì)非常緩慢。
在應(yīng)用層引入直接 I/O 需要應(yīng)用層自己管理,這帶來(lái)了額外的系統(tǒng)復(fù)雜性。
對(duì)于某些應(yīng)用程序來(lái)說(shuō),它會(huì)有它自己的數(shù)據(jù)緩存機(jī)制,比如,它會(huì)將數(shù)據(jù)緩存在應(yīng)用程序地址空間,這類應(yīng)用程序完全不需要使用操作系統(tǒng)內(nèi)核中的高速緩沖存儲(chǔ)器,這類應(yīng)用程序就被稱作是自緩存應(yīng)用程序( self-caching applications )。
例如,應(yīng)用內(nèi)部維護(hù)一個(gè)緩存空間,當(dāng)有讀操作時(shí),首先讀取應(yīng)用層的緩存數(shù)據(jù),如果沒(méi)有,那么就通過(guò) Direct I/O 直接通過(guò)磁盤 I/O 來(lái)讀取數(shù)據(jù)。緩存仍然在應(yīng)用,只不過(guò)應(yīng)用覺(jué)得自己實(shí)現(xiàn)一個(gè)緩存比操作系統(tǒng)的緩存更高效。
數(shù)據(jù)庫(kù)管理系統(tǒng)是這類應(yīng)用程序的一個(gè)代表。自緩存應(yīng)用程序傾向于使用數(shù)據(jù)的邏輯表達(dá)方式,而非物理表達(dá)方式;當(dāng)系統(tǒng)內(nèi)存較低的時(shí)候,自緩存應(yīng)用程序會(huì)讓這種數(shù)據(jù)的邏輯緩存被換出,而并非是磁盤上實(shí)際的數(shù)據(jù)被換出。自緩存應(yīng)用程序?qū)σ僮鞯臄?shù)據(jù)的語(yǔ)義了如指掌,所以它可以采用更加高效的緩存替換算法。自緩存應(yīng)用程序有可能會(huì)在多臺(tái)主機(jī)之間共享一塊內(nèi)存,那么自緩存應(yīng)用程序就需要提供一種能夠有效地將用戶地址空間的緩存數(shù)據(jù)置為無(wú)效的機(jī)制,從而確保應(yīng)用程序地址空間緩存數(shù)據(jù)的一致性。

Provider 向 Kakfa 發(fā)送消息,Kakfa 負(fù)責(zé)將消息以日志的方式持久化落盤;
Consumer 向 Kakfa 進(jìn)行拉取消息,Kafka 負(fù)責(zé)從磁盤中讀取一批日志消息,然后再通過(guò)網(wǎng)卡發(fā)送。
sendfile 避免了內(nèi)核空間到用戶空間的 CPU 全程負(fù)責(zé)的數(shù)據(jù)移動(dòng);
sendfile 基于 Page Cache 實(shí)現(xiàn),因此如果有多個(gè) Consumer 在同時(shí)消費(fèi)一個(gè)主題的消息,那么由于消息一直在 page cache 中進(jìn)行了緩存,因此只需一次磁盤 I/O,就可以服務(wù)于多個(gè) Consumer。
MySQL 的具體實(shí)現(xiàn)比 Kakfa 復(fù)雜很多,這是因?yàn)橹С?SQL 查詢的數(shù)據(jù)庫(kù)本身比消息隊(duì)列對(duì)復(fù)雜很多。

減少甚至避免用戶空間和內(nèi)核空間之間的數(shù)據(jù)拷貝:在一些場(chǎng)景下,用戶進(jìn)程在數(shù)據(jù)傳輸過(guò)程中并不需要對(duì)數(shù)據(jù)進(jìn)行訪問(wèn)和處理,那么數(shù)據(jù)在 Linux 的 Page Cache 和用戶進(jìn)程的緩沖區(qū)之間的傳輸就完全可以避免,讓數(shù)據(jù)拷貝完全在內(nèi)核里進(jìn)行,甚至可以通過(guò)更巧妙的方式避免在內(nèi)核里的數(shù)據(jù)拷貝。這一類實(shí)現(xiàn)一般是是通過(guò)增加新的系統(tǒng)調(diào)用來(lái)完成的,比如 Linux 中的 mmap(),sendfile() 以及 splice() 等。
繞過(guò)內(nèi)核的直接 I/O:允許在用戶態(tài)進(jìn)程繞過(guò)內(nèi)核直接和硬件進(jìn)行數(shù)據(jù)傳輸,內(nèi)核在傳輸過(guò)程中只負(fù)責(zé)一些管理和輔助的工作。這種方式其實(shí)和第一種有點(diǎn)類似,也是試圖避免用戶空間和內(nèi)核空間之間的數(shù)據(jù)傳輸,只是第一種方式是把數(shù)據(jù)傳輸過(guò)程放在內(nèi)核態(tài)完成,而這種方式則是直接繞過(guò)內(nèi)核和硬件通信,效果類似但原理完全不同。
內(nèi)核緩沖區(qū)和用戶緩沖區(qū)之間的傳輸優(yōu)化:這種方式側(cè)重于在用戶進(jìn)程的緩沖區(qū)和操作系統(tǒng)的頁(yè)緩存之間的 CPU 拷貝的優(yōu)化。這種方法延續(xù)了以往那種傳統(tǒng)的通信方式,但更靈活。
https://spongecaptain.cool/SimpleClearFileIO/3.%20mmap.html
https://www.ibm.com/developerworks/cn/linux/l-cn-directio/
https://spongecaptain.cool/zerocopyofmysql
推薦閱讀:
世界的真實(shí)格局分析,地球人類社會(huì)底層運(yùn)行原理
不是你需要中臺(tái),而是一名合格的架構(gòu)師(附各大廠中臺(tái)建設(shè)PPT)
企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案
論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?
企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!
【中臺(tái)實(shí)踐】華為大數(shù)據(jù)中臺(tái)架構(gòu)分享.pdf
華為如何實(shí)施數(shù)字化轉(zhuǎn)型(附PPT)
超詳細(xì)280頁(yè)Docker實(shí)戰(zhàn)文檔!開放下載
