自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

還在為慢速數(shù)據(jù)傳輸苦惱?Linux 零拷貝技術(shù)來幫你!

系統(tǒng) Linux
今天這篇文章,我就用最通俗的語言講解零拷貝的工作原理、常見實現(xiàn)方式和實際應(yīng)用,徹底幫你搞懂這項技術(shù)!

程序員的終極追求是什么?當(dāng)系統(tǒng)流量大增,用戶體驗卻絲滑依舊?沒錯!然而,在大量文件傳輸、數(shù)據(jù)傳遞的場景中,傳統(tǒng)的“數(shù)據(jù)搬運”卻拖慢了性能。為了解決這一痛點,Linux 推出了 零拷貝 技術(shù),讓數(shù)據(jù)高效傳輸幾乎無需 CPU 操心。今天,我就用最通俗的語言講解零拷貝的工作原理、常見實現(xiàn)方式和實際應(yīng)用,徹底幫你搞懂這項技術(shù)!

一、傳統(tǒng)拷貝:數(shù)據(jù)搬運的“舊時代”

為了理解零拷貝,我們先看看傳統(tǒng)數(shù)據(jù)傳輸?shù)墓ぷ鞣绞?。想象一下,我們需要把一個大文件從硬盤讀取后發(fā)送到網(wǎng)絡(luò)上。這聽起來很簡單,但實際上,傳統(tǒng)的數(shù)據(jù)傳輸涉及多個步驟并占用大量 CPU 資源。

1. 一個典型的文件傳輸過程(沒有 DMA 技術(shù)):

假設(shè)我們要將一個大文件從硬盤讀取后發(fā)送到網(wǎng)絡(luò)。以下是傳統(tǒng)拷貝方式的詳細(xì)步驟:

  • 讀取數(shù)據(jù)到內(nèi)核緩沖區(qū):使用 read() 系統(tǒng)調(diào)用,數(shù)據(jù)從硬盤讀取到內(nèi)核緩沖區(qū)。此時,CPU 需要協(xié)調(diào)和執(zhí)行相關(guān)指令來完成這一步。
  • 拷貝數(shù)據(jù)到用戶緩沖區(qū):數(shù)據(jù)從內(nèi)核緩沖區(qū)被拷貝到用戶空間的緩沖區(qū)。這一步由 read() 調(diào)用觸發(fā),CPU 完全負(fù)責(zé)這次數(shù)據(jù)拷貝。
  • 寫入數(shù)據(jù)到內(nèi)核緩沖區(qū):通過 write() 系統(tǒng)調(diào)用,數(shù)據(jù)從用戶緩沖區(qū)被再次拷貝回內(nèi)核緩沖區(qū)。CPU 再次介入并負(fù)責(zé)數(shù)據(jù)拷貝。
  • 傳輸數(shù)據(jù)到網(wǎng)卡:最終,內(nèi)核緩沖區(qū)的數(shù)據(jù)被傳輸?shù)骄W(wǎng)卡,發(fā)送到網(wǎng)絡(luò)。如果沒有 DMA 技術(shù),CPU 需要拷貝數(shù)據(jù)至網(wǎng)卡。

2. 來看個圖,更直觀點:

3. 數(shù)據(jù)傳輸?shù)摹八拇慰截悺?/h4>

在這個過程中,數(shù)據(jù)在系統(tǒng)中經(jīng)歷了四次拷貝:

  • 硬盤 -> 內(nèi)核緩沖區(qū)(CPU 參與,負(fù)責(zé)數(shù)據(jù)讀取和傳輸)
  • 內(nèi)核緩沖區(qū) -> 用戶緩沖區(qū)(read() 調(diào)用觸發(fā),CPU 負(fù)責(zé)拷貝)
  • 用戶緩沖區(qū) -> 內(nèi)核緩沖區(qū)(write() 調(diào)用觸發(fā),CPU 負(fù)責(zé)拷貝)
  • 內(nèi)核緩沖區(qū) -> 網(wǎng)卡(最終發(fā)送數(shù)據(jù),CPU 參與傳輸)

4. 性能瓶頸分析

這種傳統(tǒng)拷貝方式的問題顯而易見:

  • CPU 資源占用高:每次 read() 和 write() 調(diào)用都需要 CPU 進行多次數(shù)據(jù)拷貝,嚴(yán)重占用 CPU 資源,影響其他任務(wù)的執(zhí)行。
  • 內(nèi)存占用:當(dāng)數(shù)據(jù)量較大時,內(nèi)存使用量明顯增加,可能導(dǎo)致系統(tǒng)性能下降。
  • 上下文切換開銷:每次 read() 和 write() 調(diào)用涉及用戶態(tài)和內(nèi)核態(tài)的切換,加重了 CPU 的負(fù)擔(dān)。

這些問題在處理大文件或高頻率傳輸時尤為明顯,CPU 被迫充當(dāng)“搬運工”,性能因此受到嚴(yán)重限制。那么, 有沒有一種方法能夠減少 CPU 的“搬運”工作?此時,DMA(Direct Memory Access,直接內(nèi)存訪問)技術(shù)登場了。

二、DMA:零拷貝的前奏

DMA(Direct Memory Access,直接內(nèi)存訪問) 是一種讓數(shù)據(jù)在硬盤和內(nèi)存之間直接傳輸?shù)募夹g(shù),不需要 CPU 逐字節(jié)參與。簡單來說,DMA 是 CPU 的“好幫手”,減少了它的工作量。

1. DMA 如何幫 CPU?

在傳統(tǒng)的數(shù)據(jù)傳輸中,CPU 需要親自把數(shù)據(jù)從硬盤搬到內(nèi)存,再送到網(wǎng)絡(luò),這很耗費 CPU 資源。而 DMA 的出現(xiàn)讓 CPU 可以少干活:

  • 硬盤到內(nèi)核緩沖區(qū):由 DMA 完成,CPU 只需要下指令,DMA 就自動將數(shù)據(jù)拷貝至內(nèi)核緩沖區(qū)。
  • 內(nèi)核緩沖區(qū)到網(wǎng)卡:DMA 也能處理這部分,把數(shù)據(jù)直接送到網(wǎng)卡,CPU 只需監(jiān)督整體流程。

有了 DMA,CPU 只需要說一句:“嘿,DMA,把數(shù)據(jù)從硬盤搬到內(nèi)存去!” 然后 DMA 控制器就會接過這活,自動把數(shù)據(jù)從硬盤傳到內(nèi)核緩沖區(qū),CPU 只需要在旁邊監(jiān)督一下。

2. 有了 DMA , 再來看看數(shù)據(jù)傳輸?shù)倪^程:

為了更好地理解 DMA 在整個數(shù)據(jù)搬運中的角色,我們用圖來說明:

說明:

  • DMA 負(fù)責(zé)硬盤到內(nèi)核緩沖區(qū)和內(nèi)核到網(wǎng)卡的傳輸。
  • CPU 仍需處理內(nèi)核和用戶緩沖區(qū)之間的數(shù)據(jù)傳輸。

3. 哪些步驟仍需 CPU 參與?

雖然 DMA 能幫 CPU 分擔(dān)一些任務(wù),但它并不能全權(quán)代理所有數(shù)據(jù)拷貝工作。CPU 還是得負(fù)責(zé)以下兩件事:

  • 內(nèi)核緩沖區(qū)到用戶緩沖區(qū):數(shù)據(jù)需要被 CPU 拷貝到用戶空間供程序使用。
  • 用戶緩沖區(qū)回到內(nèi)核緩沖區(qū):程序處理完數(shù)據(jù)后,CPU 還得把數(shù)據(jù)拷回內(nèi)核,準(zhǔn)備進行后續(xù)傳輸。

就像請了一個幫手,但有些細(xì)致活兒還得自己干。所以,在高并發(fā)或大文件傳輸時,CPU 依舊會因為這些拷貝任務(wù)感到壓力。

4. 總結(jié)一下

總結(jié)來說,DMA 確實減輕了 CPU 在數(shù)據(jù)傳輸中的負(fù)擔(dān),讓數(shù)據(jù)從硬盤傳輸?shù)絻?nèi)核緩沖區(qū)和內(nèi)核緩沖區(qū)到網(wǎng)卡時幾乎無需 CPU 的參與。然而,DMA 無法徹底解決數(shù)據(jù)在內(nèi)核和用戶空間之間的拷貝問題。CPU 依然需要進行兩次數(shù)據(jù)搬運,特別是在高并發(fā)和大文件傳輸場景下,這個限制變得尤為突出。

三、零拷貝:讓數(shù)據(jù)“直達(dá)”

因此,為了進一步減少 CPU 的參與,提升傳輸效率,Linux 推出了 零拷貝 技術(shù)。這項技術(shù)的核心目標(biāo)是:讓數(shù)據(jù)在內(nèi)核空間內(nèi)直接流轉(zhuǎn),避免在用戶空間的冗余拷貝,從而最大限度減少 CPU 的內(nèi)存拷貝操作,提高系統(tǒng)性能。

接下來,我們來詳細(xì)看看 Linux 中的幾種主要零拷貝實現(xiàn)方式:

注意:Linux 中零拷貝技術(shù)的實現(xiàn)需要硬件支持 DMA。

1. sendfile:最早的零拷貝方式

sendfile 是最早在 Linux 中引入的零拷貝方式,專為文件傳輸設(shè)計。

2. sendfile 的工作流程

  • DMA(直接內(nèi)存訪問)直接將文件數(shù)據(jù)加載到內(nèi)核緩沖區(qū)。
  • 數(shù)據(jù)從內(nèi)核緩沖區(qū)直接進入網(wǎng)絡(luò)協(xié)議棧中的 socket 內(nèi)核緩沖區(qū)。
  • 數(shù)據(jù)通過網(wǎng)絡(luò)協(xié)議棧處理后,通過網(wǎng)卡直接發(fā)往網(wǎng)絡(luò)。

通過 sendfile,整個傳輸過程 CPU 只需要一次數(shù)據(jù)拷貝,減少了 CPU 的使用。

3. 簡單圖解:

sendfile 圖解說明:

  • 從硬盤讀取數(shù)據(jù):文件數(shù)據(jù)通過 DMA 從硬盤讀取,直接加載到內(nèi)核緩沖區(qū),這個過程不需要 CPU 的參與。
  • 拷貝數(shù)據(jù)至網(wǎng)絡(luò)協(xié)議棧的 socket 緩沖區(qū):數(shù)據(jù)不進入用戶空間,而是從內(nèi)核緩沖區(qū)直接進入網(wǎng)絡(luò)協(xié)議棧中的 socket 緩沖區(qū),在這里經(jīng)過必要的協(xié)議處理(如 TCP/IP 封裝)。
  • 數(shù)據(jù)通過網(wǎng)卡發(fā)送:數(shù)據(jù)最終通過網(wǎng)卡直接發(fā)往網(wǎng)絡(luò)。

4. sendfile 接口說明

sendfile函數(shù)定義如下:

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
  • out_fd:目標(biāo)文件描述符,一般是 socket 描述符,用于網(wǎng)絡(luò)發(fā)送。
  • in_fd:源文件描述符,通常是從硬盤讀取的文件。
  • offset:偏移量指針,用于指定從文件的哪個位置開始讀取。如果為 NULL,則從當(dāng)前偏移位置開始讀取。
  • count:要傳輸?shù)淖止?jié)數(shù)。

返回值是實際傳輸?shù)淖止?jié)數(shù),出錯時返回 -1,并設(shè)置 errno 來指示錯誤原因。

5. 簡單代碼示例

#include <sys/sendfile.h>

int main() {
    int input_fd = open("input.txt", O_RDONLY);
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    bind(server_fd, (struct sockaddr *)&address, sizeof(address));
    listen(server_fd, 3);
    int client_fd = accept(server_fd, NULL, NULL);

    sendfile(client_fd, input_fd, NULL, 1024);

    close(input_fd);
    close(client_fd);
    close(server_fd);

    return 0;
}

這個例子展示了如何使用 sendfile 將本地文件發(fā)送到一個通過網(wǎng)絡(luò)連接的客戶端。只需要調(diào)用 sendfile,數(shù)據(jù)就能從 input_fd 直接傳輸?shù)?nbsp;output_fd。

6. 適用場景

sendfile 主要用于將文件數(shù)據(jù)直接傳輸?shù)骄W(wǎng)絡(luò),非常適合需要高效傳輸大文件的情況,例如文件服務(wù)器、流媒體傳輸、備份系統(tǒng)等。

在傳統(tǒng)的數(shù)據(jù)傳輸方式中,數(shù)據(jù)需要經(jīng)過多個步驟:

  • 首先,數(shù)據(jù)從硬盤讀取到內(nèi)核空間。
  • 然后,數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間。
  • 最后,數(shù)據(jù)從用戶空間再拷貝回內(nèi)核,送到網(wǎng)卡發(fā)出去。

總結(jié)來說: sendfile 可以讓數(shù)據(jù)傳輸更加高效,減少 CPU 的干預(yù),特別適合簡單的大文件傳輸場景。然而,如果遇到更復(fù)雜的傳輸需求,比如要在多個不同類型的文件描述符之間移動數(shù)據(jù),splice 則提供了一種更加靈活的方法。接下來我們來看看 splice 是如何實現(xiàn)這一點的。

四、splice :管道式零拷貝

splice 是 Linux 中另一種實現(xiàn)零拷貝的數(shù)據(jù)傳輸系統(tǒng)調(diào)用,專為在不同類型的文件描述符之間高效地移動數(shù)據(jù)而設(shè)計,適用于在內(nèi)核中直接傳輸數(shù)據(jù),減少不必要的拷貝。

1. splice 的工作流程

  • 從文件讀取數(shù)據(jù):使用 splice 系統(tǒng)調(diào)用將數(shù)據(jù)從輸入文件描述符(例如硬盤文件)讀取,數(shù)據(jù)直接通過 DMA(直接內(nèi)存訪問)進入內(nèi)核緩沖區(qū)。
  • 傳輸?shù)骄W(wǎng)絡(luò) socket:隨后,splice 繼續(xù)將內(nèi)核緩沖區(qū)中的數(shù)據(jù)直接傳輸?shù)侥繕?biāo)網(wǎng)絡(luò) socket 的文件描述符中。

整個過程在內(nèi)核空間內(nèi)完成,避免了數(shù)據(jù)從內(nèi)核空間到用戶空間的往返拷貝,大大減少了 CPU 的參與,提高了系統(tǒng)性能。

2. 簡單圖解:

和 sendfile 圖解類似,只是接口不一樣。

splice 圖解說明:

數(shù)據(jù)通過 splice 從文件描述符傳輸?shù)骄W(wǎng)絡(luò) socket。數(shù)據(jù)首先通過 DMA 進入內(nèi)核緩沖區(qū),然后直接傳輸?shù)骄W(wǎng)絡(luò) socket,整個過程避免了用戶空間的介入,顯著減少了 CPU 的拷貝工作。

3. splice 接口說明

splice 函數(shù)的定義如下:

ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);
  • fd_in:源文件描述符,數(shù)據(jù)從這里讀取。
  • off_in:指向源偏移量的指針,如果為 NULL,則使用當(dāng)前偏移量。
  • fd_out:目標(biāo)文件描述符,數(shù)據(jù)將被寫入這里。
  • off_out:指向目標(biāo)偏移量的指針,如果為 NULL,則使用當(dāng)前偏移量。
  • len:要傳輸?shù)淖止?jié)數(shù)。
  • flags:控制行為的標(biāo)志,例如 SPLICE_F_MOVE、SPLICE_F_MORE 等。

返回值是實際傳輸?shù)淖止?jié)數(shù),出錯時返回 -1,并設(shè)置 errno 來指示錯誤原因。

4. 簡單代碼示例

int main() {
    int input_fd = open("input.txt", O_RDONLY);
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    bind(server_fd, (struct sockaddr *)&address, sizeof(address));
    listen(server_fd, 3);
    int client_fd = accept(server_fd, NULL, NULL);

    splice(input_fd, NULL, client_fd, NULL, 1024, SPLICE_F_MORE);

    close(input_fd);
    close(client_fd);
    close(server_fd);

    return 0;
}

這個例子展示了如何使用 splice 將本地文件直接發(fā)送到網(wǎng)絡(luò) socket,以實現(xiàn)高效的數(shù)據(jù)傳輸。

5. 適用場景

splice 適用于在文件描述符之間進行高效、直接的數(shù)據(jù)傳輸,例如從文件到網(wǎng)絡(luò) socket 的傳輸,或在文件、管道和 socket 之間傳遞數(shù)據(jù)。在這種情況下,數(shù)據(jù)在內(nèi)核空間內(nèi)完成傳輸,無需進入用戶空間,從而顯著減少拷貝次數(shù)和 CPU 的參與。另外 splice 特別適合需要靈活數(shù)據(jù)流動和減少 CPU 負(fù)擔(dān)的場景,例如日志處理、實時數(shù)據(jù)流處理等。

6. sendfile 與 splice 的區(qū)別

雖然 sendfile 和 splice 都是 Linux 提供的零拷貝技術(shù),用于高效地在內(nèi)核空間傳輸數(shù)據(jù),但它們在應(yīng)用場景和功能上存在一些顯著區(qū)別:

數(shù)據(jù)流動方式:

  • sendfile:直接將文件中的數(shù)據(jù)從內(nèi)核緩沖區(qū)傳輸?shù)?socket 緩沖區(qū),適合文件到網(wǎng)絡(luò)的傳輸。適合需要簡單高效的文件到網(wǎng)絡(luò)的傳輸場景。
  • splice:更靈活,可以在任意文件描述符之間進行數(shù)據(jù)傳輸,包括文件、管道、socket 等。因此,splice 可以在文件、管道和 socket 之間實現(xiàn)更復(fù)雜的數(shù)據(jù)流轉(zhuǎn)。

適用場景:

  • sendfile:主要用于文件到網(wǎng)絡(luò)的傳輸,非常適合文件服務(wù)器、流媒體等需要高效傳輸文件的場景。
  • splice:更適合復(fù)雜的數(shù)據(jù)流動場景,例如在文件、管道和網(wǎng)絡(luò)之間需要多步傳輸或靈活控制數(shù)據(jù)流向的情況。

靈活性:

  • sendfile:用于直接、高效地將文件發(fā)送到網(wǎng)絡(luò),雖然操作單一,但性能非常高效。
  • splice:可以結(jié)合管道使用,實現(xiàn)更復(fù)雜的數(shù)據(jù)流向控制,例如先通過管道對數(shù)據(jù)進行處理,再發(fā)送到目標(biāo)位置。

五、mmap + write:映射式零拷貝

除了以上兩種方式,mmap + write 也是一種常見的零拷貝實現(xiàn)方式。這種方式主要是通過內(nèi)存映射來減少數(shù)據(jù)拷貝的步驟。

1. mmap + write 的工作流程

使用 mmap 系統(tǒng)調(diào)用將文件映射到進程的虛擬地址空間中,這樣數(shù)據(jù)就可以直接在內(nèi)核空間和用戶空間共享,而不需要額外的拷貝操作。

使用 write 系統(tǒng)調(diào)用將映射的內(nèi)存區(qū)域直接寫入到目標(biāo)文件描述符中(比如網(wǎng)絡(luò) socket),完成數(shù)據(jù)傳輸。

這種方式減少了數(shù)據(jù)拷貝,提高了效率,適合需要靈活操作數(shù)據(jù)后再發(fā)送的場景。通過這種方式,數(shù)據(jù)不需要顯式地從內(nèi)核空間拷貝到用戶空間,而是通過映射的方式共享,從而減少了不必要的拷貝。

2. 簡單圖解:

mmap + write 圖解說明:

  • 使用 mmap 將文件數(shù)據(jù)映射到進程的虛擬地址空間,避免顯式的數(shù)據(jù)拷貝。
  • 通過 write 直接將映射的內(nèi)存區(qū)域數(shù)據(jù)發(fā)送到目標(biāo)文件描述符(如網(wǎng)絡(luò) socket)。

3. mmap 接口說明

mmap 函數(shù)的定義如下:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • addr:指定映射內(nèi)存的起始地址,通常為 NULL 由系統(tǒng)決定。
  • length:要映射的內(nèi)存區(qū)域的大小。
  • prot:映射區(qū)域的保護標(biāo)志,例如 PROT_READ、PROT_WRITE。
  • flags:影響映射的屬性,例如 MAP_SHARED、MAP_PRIVATE。
  • fd:文件描述符,指向需要映射的文件。
  • offset:文件中的偏移量,表示從文件的哪個位置開始映射。

返回值為映射內(nèi)存區(qū)域的指針,出錯時返回 MAP_FAILED,并設(shè)置 errno。

4. 簡單代碼示例

int main() {
    int input_fd = open("input.txt", O_RDONLY);
    struct stat file_stat;
    fstat(input_fd, &file_stat);

    char *mapped = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, input_fd, 0);

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    bind(server_fd, (struct sockaddr *)&address, sizeof(address));
    listen(server_fd, 3);
    int client_fd = accept(server_fd, NULL, NULL);

    write(client_fd, mapped, file_stat.st_size);

    munmap(mapped, file_stat.st_size);
    close(input_fd);
    close(client_fd);
    close(server_fd);

    return 0;
}

這個例子展示了如何使用 mmap 將文件映射到內(nèi)存,然后通過 write 將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)連接的客戶端。

5. 適用場景

mmap + write 適用于需要對文件數(shù)據(jù)進行靈活操作的場景,例如需要在發(fā)送數(shù)據(jù)前進行修改或部分處理。與 sendfile 相比,mmap + write 提供了更大的靈活性,因為它允許在用戶態(tài)訪問數(shù)據(jù)內(nèi)容,這對于需要對文件進行預(yù)處理的應(yīng)用場景非常有用,例如壓縮、加密或者數(shù)據(jù)轉(zhuǎn)換等。

然而,這種方式也帶來了更多的開銷,因為數(shù)據(jù)需要在用戶態(tài)和內(nèi)核態(tài)之間進行交互,這會增加系統(tǒng)調(diào)用的成本。因此,mmap + write 更適合那些需要在數(shù)據(jù)傳輸前進行一些自定義處理的情況,而不太適合純粹的大文件高效傳輸。

六、tee:數(shù)據(jù)復(fù)制的零拷貝方式

tee 是 Linux 中的一種零拷貝方式,它可以把一個管道中的數(shù)據(jù)復(fù)制到另一個管道,同時保留原管道中的數(shù)據(jù)。這意味著數(shù)據(jù)可以同時被發(fā)送到多個目標(biāo),而不影響原來的數(shù)據(jù)流,非常適合日志記錄和實時數(shù)據(jù)分析等需要把同樣的數(shù)據(jù)送往不同地方的場景。

1. tee 的工作流程

數(shù)據(jù)復(fù)制到另一個管道:tee 系統(tǒng)調(diào)用可以將一個管道中的數(shù)據(jù)復(fù)制到另一個管道,而不改變原有的數(shù)據(jù)。這意味著數(shù)據(jù)可以在內(nèi)核空間中被同時用于不同的目的,而無需經(jīng)過用戶空間的拷貝。

2. tee 接口說明

tee 函數(shù)的定義如下:

ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);
  • fd_in:源管道文件描述符,數(shù)據(jù)從這里讀取。
  • fd_out:目標(biāo)管道文件描述符,數(shù)據(jù)將被寫入這里。
  • len:要復(fù)制的字節(jié)數(shù)。
  • flags:控制行為的標(biāo)志,例如 SPLICE_F_NONBLOCK 等。

返回值是實際復(fù)制的字節(jié)數(shù),出錯時返回 -1,并設(shè)置 errno 來指示錯誤原因。

3. 簡單代碼示例

int main() {
    int pipe_fd[2];
    pipe(pipe_fd);

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    bind(server_fd, (struct sockaddr *)&address, sizeof(address));
    listen(server_fd, 3);
    int client_fd = accept(server_fd, NULL, NULL);

    // 使用 tee 復(fù)制數(shù)據(jù)
    tee(pipe_fd[0], pipe_fd[1], 1024, 0);
    splice(pipe_fd[0], NULL, client_fd, NULL, 1024, SPLICE_F_MORE);

    close(pipe_fd[0]);
    close(pipe_fd[1]);
    close(client_fd);
    close(server_fd);

    return 0;
}

這個例子展示了如何使用 tee 將管道中的數(shù)據(jù)復(fù)制,并通過 splice 將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò) socket,從而實現(xiàn)高效的數(shù)據(jù)傳輸和復(fù)制。

4. 適用場景

tee 非常適合需要將數(shù)據(jù)同時發(fā)送到多個目標(biāo)的場景,比如實時數(shù)據(jù)處理、日志記錄等。通過 tee,可以在內(nèi)核空間內(nèi)實現(xiàn)多目標(biāo)數(shù)據(jù)復(fù)制,提高系統(tǒng)性能,減少 CPU 負(fù)擔(dān)。

總結(jié)對比:

下面我將 Linux 的幾種零拷貝方式做了總結(jié),方便大家對比學(xué)習(xí):

方法

描述

零拷貝類型

CPU 參與度

適用場景

sendfile

直接將文件數(shù)據(jù)發(fā)送到套接字,無需拷貝到用戶空間。

完全零拷貝

極少,數(shù)據(jù)直接傳輸。

文件服務(wù)器、視頻流傳輸?shù)却笪募鼍啊?/p>

splice

在內(nèi)核空間內(nèi)高效地在文件描述符之間傳輸數(shù)據(jù)。

完全零拷貝

極少,完全在內(nèi)核內(nèi)。

文件、管道與 socket 之間的復(fù)雜傳輸場景。

mmap + write

將文件映射到內(nèi)存并使用 write 發(fā)送數(shù)據(jù),靈活處理數(shù)據(jù)

部分零拷貝

中等,需要映射和寫入。

數(shù)據(jù)需要處理或修改的場景,如壓縮加密。

tee

將管道中的數(shù)據(jù)復(fù)制到另一個管道,無需消耗原始數(shù)據(jù)。

完全零拷貝

極少,數(shù)據(jù)復(fù)制在內(nèi)核。

日志處理、實時數(shù)據(jù)監(jiān)控等多目標(biāo)場景。

最后

希望這篇文章讓你對 Linux 的零拷貝技術(shù)有了更全面、更清晰的了解!這些技術(shù)看起來可能有些復(fù)雜,但一旦掌握后,你會發(fā)現(xiàn)它們非常簡單, 并且在實際項目中非常實用。

責(zé)任編輯:趙寧寧 來源: 跟著小康學(xué)編程
相關(guān)推薦

2022-02-24 07:03:13

JavaScrip語言

2020-06-12 07:50:15

大數(shù)據(jù)

2020-05-20 14:38:35

傳輸距離網(wǎng)絡(luò)布線

2013-01-17 14:03:32

英特爾光纖數(shù)據(jù)傳輸

2010-04-07 14:54:38

2023-09-14 08:46:27

零拷貝I/O異步

2020-08-06 08:06:46

物聯(lián)網(wǎng)數(shù)據(jù)技術(shù)

2013-12-10 10:21:52

光纜數(shù)據(jù)傳輸脈沖

2010-07-13 15:55:12

FTP數(shù)據(jù)傳輸模式

2023-04-12 16:20:00

同步數(shù)據(jù)異步數(shù)據(jù)傳輸

2013-11-26 15:51:45

Android編程藍(lán)牙數(shù)據(jù)傳輸

2015-10-14 09:44:55

TCP網(wǎng)絡(luò)協(xié)議數(shù)據(jù)傳輸

2009-12-08 11:17:41

WCF雙向通信

2009-07-07 16:46:33

數(shù)據(jù)傳輸銅纜結(jié)構(gòu)

2021-12-14 11:01:44

TCPUDP網(wǎng)絡(luò)協(xié)議

2021-06-09 11:28:06

加密數(shù)據(jù)Jsencrypt

2019-09-06 09:11:36

以太網(wǎng)數(shù)據(jù)二層交換

2017-05-04 12:48:18

WOT網(wǎng)易NDC

2024-08-05 09:31:00

MySQLDTS數(shù)據(jù)

2022-03-30 15:06:25

數(shù)據(jù)傳輸Harmony源碼分析
點贊
收藏

51CTO技術(shù)棧公眾號