偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

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

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

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

1. 一個(gè)典型的文件傳輸過(guò)程(沒(méi)有 DMA 技術(shù)):

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

  • 讀取數(shù)據(jù)到內(nèi)核緩沖區(qū):使用 read() 系統(tǒng)調(diào)用,數(shù)據(jù)從硬盤(pán)讀取到內(nèi)核緩沖區(qū)。此時(shí),CPU 需要協(xié)調(diào)和執(zhí)行相關(guān)指令來(lái)完成這一步。
  • 拷貝數(shù)據(jù)到用戶(hù)緩沖區(qū):數(shù)據(jù)從內(nèi)核緩沖區(qū)被拷貝到用戶(hù)空間的緩沖區(qū)。這一步由 read() 調(diào)用觸發(fā),CPU 完全負(fù)責(zé)這次數(shù)據(jù)拷貝。
  • 寫(xiě)入數(shù)據(jù)到內(nèi)核緩沖區(qū):通過(guò) write() 系統(tǒng)調(diào)用,數(shù)據(jù)從用戶(hù)緩沖區(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ò)。如果沒(méi)有 DMA 技術(shù),CPU 需要拷貝數(shù)據(jù)至網(wǎng)卡。

2. 來(lái)看個(gè)圖,更直觀點(diǎn):

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

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

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

4. 性能瓶頸分析

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

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

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

二、DMA:零拷貝的前奏

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

1. DMA 如何幫 CPU?

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

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

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

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

為了更好地理解 DMA 在整個(gè)數(shù)據(jù)搬運(yùn)中的角色,我們用圖來(lái)說(shuō)明:

說(shuō)明:

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

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

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

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

就像請(qǐng)了一個(gè)幫手,但有些細(xì)致活兒還得自己干。所以,在高并發(fā)或大文件傳輸時(shí),CPU 依舊會(huì)因?yàn)檫@些拷貝任務(wù)感到壓力。

4. 總結(jié)一下

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

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

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

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

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

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

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

2. sendfile 的工作流程

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

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

3. 簡(jiǎn)單圖解:

sendfile 圖解說(shuō)明:

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

4. sendfile 接口說(shuō)明

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:源文件描述符,通常是從硬盤(pán)讀取的文件。
  • offset:偏移量指針,用于指定從文件的哪個(gè)位置開(kāi)始讀取。如果為 NULL,則從當(dāng)前偏移位置開(kāi)始讀取。
  • count:要傳輸?shù)淖止?jié)數(shù)。

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

5. 簡(jiǎn)單代碼示例

#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;
}

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

6. 適用場(chǎng)景

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

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

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

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

四、splice :管道式零拷貝

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

1. splice 的工作流程

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

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

2. 簡(jiǎn)單圖解:

和 sendfile 圖解類(lèi)似,只是接口不一樣。

splice 圖解說(shuō)明:

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

3. splice 接口說(shuō)明

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ù)將被寫(xiě)入這里。
  • off_out:指向目標(biāo)偏移量的指針,如果為 NULL,則使用當(dāng)前偏移量。
  • len:要傳輸?shù)淖止?jié)數(shù)。
  • flags:控制行為的標(biāo)志,例如 SPLICE_F_MOVE、SPLICE_F_MORE 等。

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

4. 簡(jiǎn)單代碼示例

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;
}

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

5. 適用場(chǎng)景

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

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

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

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

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

適用場(chǎng)景:

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

靈活性:

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

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

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

1. mmap + write 的工作流程

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

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

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

2. 簡(jiǎn)單圖解:

mmap + write 圖解說(shuō)明:

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

3. mmap 接口說(shuō)明

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ū)域的保護(hù)標(biāo)志,例如 PROT_READ、PROT_WRITE。
  • flags:影響映射的屬性,例如 MAP_SHARED、MAP_PRIVATE。
  • fd:文件描述符,指向需要映射的文件。
  • offset:文件中的偏移量,表示從文件的哪個(gè)位置開(kāi)始映射。

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

4. 簡(jiǎn)單代碼示例

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;
}

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

5. 適用場(chǎng)景

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

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

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

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

1. tee 的工作流程

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

2. tee 接口說(shuō)明

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ù)將被寫(xiě)入這里。
  • len:要復(fù)制的字節(jié)數(shù)。
  • flags:控制行為的標(biāo)志,例如 SPLICE_F_NONBLOCK 等。

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

3. 簡(jiǎn)單代碼示例

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;
}

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

4. 適用場(chǎng)景

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

總結(jié)對(duì)比:

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

方法

描述

零拷貝類(lèi)型

CPU 參與度

適用場(chǎng)景

sendfile

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

完全零拷貝

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

文件服務(wù)器、視頻流傳輸?shù)却笪募?chǎng)景。

splice

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

完全零拷貝

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

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

mmap + write

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

部分零拷貝

中等,需要映射和寫(xiě)入。

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

tee

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

完全零拷貝

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

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

最后

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

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

2022-02-24 07:03:13

JavaScrip語(yǔ)言

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

2020-08-06 08:06:46

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

2023-09-14 08:46:27

零拷貝I/O異步

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-06-09 11:28:06

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

2021-12-14 11:01:44

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

2021-10-08 08:37:38

數(shù)據(jù)傳輸數(shù)據(jù)調(diào)用網(wǎng)絡(luò)協(xié)議

2011-03-02 11:23:48

2019-09-06 09:11:36

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

2024-08-05 09:31:00

MySQLDTS數(shù)據(jù)
點(diǎn)贊
收藏

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