1.前言
像大白這種調包俠,深知不懂底層技術點就如同空中樓閣,再這樣下去面阿里p10是沒希望了。
想到這里,我開始慌了,所以今天和大家一起學習個底層技術點-零拷貝Zero-Copy。
Linux系統(tǒng)中一切皆文件,仔細想一下Linux系統(tǒng)的很多活動無外乎讀操作和寫操作,零拷貝就是為了提高讀寫性能而出現(xiàn)的。
廢話不多說,馬上開大車,走起!
2. 數(shù)據(jù)拷貝基礎過程
在Linux系統(tǒng)內部緩存和內存容量都是有限的,更多的數(shù)據(jù)都是存儲在磁盤中。對于Web服務器來說,經常需要從磁盤中讀取數(shù)據(jù)到內存,然后再通過網卡傳輸給用戶:
上述數(shù)據(jù)流轉只是大框,接下來看看幾種模式。
2.1 僅CPU方式
-
當應用程序需要讀取磁盤數(shù)據(jù)時,調用read()從用戶態(tài)陷入內核態(tài),read()這個系統(tǒng)調用最終由CPU來完成; -
CPU向磁盤發(fā)起I/O請求,磁盤收到之后開始準備數(shù)據(jù); -
磁盤將數(shù)據(jù)放到磁盤緩沖區(qū)之后,向CPU發(fā)起I/O中斷,報告CPU數(shù)據(jù)已經Ready了; -
CPU收到磁盤控制器的I/O中斷之后,開始拷貝數(shù)據(jù),完成之后read()返回,再從內核態(tài)切換到用戶態(tài);
2.2 CPU&DMA方式
CPU的時間寶貴,讓它做雜活就是浪費資源。
直接內存訪問(Direct Memory Access),是一種硬件設備繞開CPU獨立直接訪問內存的機制。所以DMA在一定程度上解放了CPU,把之前CPU的雜活讓硬件直接自己做了,提高了CPU效率。
目前支持DMA的硬件包括:網卡、聲卡、顯卡、磁盤控制器等。
有了DMA的參與之后的流程發(fā)生了一些變化:
最主要的變化是,CPU不再和磁盤直接交互,而是DMA和磁盤交互并且將數(shù)據(jù)從磁盤緩沖區(qū)拷貝到內核緩沖區(qū),之后的過程類似。
“【敲黑板】無論從僅CPU方式和DMA&CPU方式,都存在多次冗余數(shù)據(jù)拷貝和內核態(tài)&用戶態(tài)的切換。
”
我們繼續(xù)思考Web服務器讀取本地磁盤文件數(shù)據(jù)再通過網絡傳輸給用戶的詳細過程。
3.普通模式數(shù)據(jù)交互
一次完成的數(shù)據(jù)交互包括幾個部分:系統(tǒng)調用syscall、CPU、DMA、網卡、磁盤等。
系統(tǒng)調用syscall是應用程序和內核交互的橋梁,每次進行調用/返回就會產生兩次切換:
-
調用syscall 從用戶態(tài)切換到內核態(tài) -
syscall返回 從內核態(tài)切換到用戶態(tài)
來看下完整的數(shù)據(jù)拷貝過程簡圖:
讀數(shù)據(jù)過程:
-
應用程序要讀取磁盤數(shù)據(jù),調用read()函數(shù)從而實現(xiàn)用戶態(tài)切換內核態(tài),這是第1次狀態(tài)切換; -
DMA控制器將數(shù)據(jù)從磁盤拷貝到內核緩沖區(qū),這是第1次DMA拷貝; -
CPU將數(shù)據(jù)從內核緩沖區(qū)復制到用戶緩沖區(qū),這是第1次CPU拷貝; -
CPU完成拷貝之后,read()函數(shù)返回實現(xiàn)用戶態(tài)切換用戶態(tài),這是第2次狀態(tài)切換;
寫數(shù)據(jù)過程:
-
應用程序要向網卡寫數(shù)據(jù),調用write()函數(shù)實現(xiàn)用戶態(tài)切換內核態(tài),這是第1次切換; -
CPU將用戶緩沖區(qū)數(shù)據(jù)拷貝到內核緩沖區(qū),這是第1次CPU拷貝; -
DMA控制器將數(shù)據(jù)從內核緩沖區(qū)復制到socket緩沖區(qū),這是第1次DMA拷貝; -
完成拷貝之后,write()函數(shù)返回實現(xiàn)內核態(tài)切換用戶態(tài),這是第2次切換;
綜上所述:
-
讀過程涉及2次空間切換、1次DMA拷貝、1次CPU拷貝; -
寫過程涉及2次空間切換、1次DMA拷貝、1次CPU拷貝;
4. 零拷貝技術
4.1 出現(xiàn)原因
我們可以看到,如果應用程序不對數(shù)據(jù)做修改,從內核緩沖區(qū)到用戶緩沖區(qū),再從用戶緩沖區(qū)到內核緩沖區(qū)。兩次數(shù)據(jù)拷貝都需要CPU的參與,并且涉及用戶態(tài)與內核態(tài)的多次切換,加重了CPU負擔。
我們需要降低冗余數(shù)據(jù)拷貝、解放CPU,這也就是零拷貝Zero-Copy技術。
4.2 解決思路
目前來看,零拷貝技術的幾個實現(xiàn)手段包括:mmap+write、sendfile、sendfile+DMA收集、splice等。
4.2.1 mmap方式
mmap是Linux提供的一種內存映射文件的機制,它實現(xiàn)了將內核中讀緩沖區(qū)地址與用戶空間緩沖區(qū)地址進行映射,從而實現(xiàn)內核緩沖區(qū)與用戶緩沖區(qū)的共享。
這樣就減少了一次用戶態(tài)和內核態(tài)的CPU拷貝,但是在內核空間內仍然有一次CPU拷貝。
mmap對大文件傳輸有一定優(yōu)勢,但是小文件可能出現(xiàn)碎片,并且在多個進程同時操作文件時可能產生引發(fā)coredump的signal。
4.2.2 sendfile方式
mmap+write方式有一定改進,但是由系統(tǒng)調用引起的狀態(tài)切換并沒有減少。
sendfile系統(tǒng)調用是在 Linux 內核2.1版本中被引入,它建立了兩個文件之間的傳輸通道。
sendfile方式只使用一個函數(shù)就可以完成之前的read+write 和 mmap+write的功能,這樣就少了2次狀態(tài)切換,由于數(shù)據(jù)不經過用戶緩沖區(qū),因此該數(shù)據(jù)無法被修改。
從圖中可以看到,應用程序只需要調用sendfile函數(shù)即可完成,只有2次狀態(tài)切換、1次CPU拷貝、2次DMA拷貝。
但是sendfile在內核緩沖區(qū)和socket緩沖區(qū)仍然存在一次CPU拷貝,或許這個還可以優(yōu)化。
4.2.3 sendfile+DMA收集
Linux 2.4 內核對 sendfile 系統(tǒng)調用進行優(yōu)化,但是需要硬件DMA控制器的配合。
升級后的sendfile將內核空間緩沖區(qū)中對應的數(shù)據(jù)描述信息(文件描述符、地址偏移量等信息)記錄到socket緩沖區(qū)中。
DMA控制器根據(jù)socket緩沖區(qū)中的地址和偏移量將數(shù)據(jù)從內核緩沖區(qū)拷貝到網卡中,從而省去了內核空間中僅剩1次CPU拷貝。
這種方式有2次狀態(tài)切換、0次CPU拷貝、2次DMA拷貝,但是仍然無法對數(shù)據(jù)進行修改,并且需要硬件層面DMA的支持,并且sendfile只能將文件數(shù)據(jù)拷貝到socket描述符上,有一定的局限性。
4.2.4 splice方式
splice系統(tǒng)調用是Linux 在 2.6 版本引入的,其不需要硬件支持,并且不再限定于socket上,實現(xiàn)兩個普通文件之間的數(shù)據(jù)零拷貝。
splice 系統(tǒng)調用可以在內核緩沖區(qū)和socket緩沖區(qū)之間建立管道來傳輸數(shù)據(jù),避免了兩者之間的 CPU 拷貝操作。
splice也有一些局限,它的兩個文件描述符參數(shù)中有一個必須是管道設備。
5.本文小結
本文通過介紹數(shù)據(jù)交互的基本過程、傳統(tǒng)模式的缺點,進而介紹了零拷貝的一些實現(xiàn)方法。
零拷貝技術是非常底層且重要的讀寫優(yōu)化,對于服務并發(fā)能力的提升有很大幫助,就這么多吧,下期再見!
免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!





