日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux网络编程 | 零拷贝 :sendfile、mmap、splice、tee

發布時間:2024/4/11 linux 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux网络编程 | 零拷贝 :sendfile、mmap、splice、tee 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 傳統文件傳輸的問題
  • Linux中實現零拷貝的方法


傳統文件傳輸的問題

在網絡編程中,如果我們想要提供文件傳輸的功能,最簡單的方法就是用read將數據從磁盤上的文件中讀取出來,再將其用write寫入到socket中,通過網絡協議發送給客戶端。

ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count);

但是就是這兩個簡單的操作,卻帶來了大量的性能丟失

例如我們的服務器需要為客戶端提供一個下載操作,此時的操作如下

從上圖可以看出,雖然僅僅只有這兩行代碼,但是卻在發生了四次用戶態和內核態的上下文切換,以及四次數據拷貝,也就是在這個地方產生了大量不必要的損耗。

那么為什么會發生這些操作呢?

上下文切換

由于read和recv是系統調用,所以每次調用該函數我們都需要從用戶態切換至內核態,等待內核完成任務后再從內核態切換回用戶態。

數據拷貝
上面也說了,由于數據的讀取與寫入都是由系統進行的,那么我們就得將數據從用戶的緩沖區中拷貝到內核,

  • 第一次拷貝:將磁盤中的數據拷貝到內核的緩沖區中
  • 第二次拷貝:內核將數據處理完,接著拷貝到用戶緩沖區中
  • 第三次拷貝:此時需要通過socket將數據發送出去,將用戶緩沖區中的數據拷貝至內核中socket的緩沖區中
  • 第四次拷貝:把內核中socket緩沖區的數據拷貝到網卡的緩沖區中,通過網卡將數據發送出去。

所以要想優化傳輸性能,就要從減少數據拷貝和用戶態內核態的上下文切換下手,這也就是零拷貝技術的由來。

什么是零拷貝呢?
零拷貝的主要任務就是避免CPU將數據從一塊存儲中拷貝到另一塊存儲,主要就是利用各種技術,避免讓CPU做大量的數據拷貝任務,以此減少不必要的拷貝。或者借助其他的一些組件來完成簡單的數據傳輸任務,讓CPU解脫出來專注別的任務,使得系統資源的利用更加有效

Linux中實現零拷貝的方法

Linux中實現零拷貝的方法主要有以下幾種,下面一一對其進行介紹

  • sendfile
  • mmap
  • splice
  • tee
  • sendfile
    sendfile函數的作用是直接在兩個文件描述符之間傳遞數據。由于整個操作完全在內核中(直接從內核緩沖區拷貝到socket緩沖區),從而避免了內核緩沖區和用戶緩沖區之間的數據拷貝。

    需要注意的是,in_fd必須是一個支持類似mmap函數的文件描述符,不能是socket或者管道,而out_fd必須是一個socket,由此可見sendfile是專門為了在網絡上傳輸文件而實現的函數。

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

    參數:
    out_fd : 待寫入內容的文件描述符
    in_fd : 待讀出內容的文件描述符
    offset : 文件的偏移量
    count : 需要傳輸的字節數

    返回值:
    成功:返回傳輸的字節數
    失敗:返回-1并設置errno


    mmap
    mmap用于申請一段內存空間,也就是我們在進程間通信中提到過的共享內存,通過將內核緩沖區的數據映射到用戶空間中,兩者通過共享緩沖區直接訪問統一資源,此時內核與用戶空間就不需要再進行任何的數據拷貝操作了

    其中mmap用于申請空間,額munmap用于釋放這段空間。

    #include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); int munmap(void *addr, size_t length);

    參數:
    addr : 內存的起始地址,如果設置為空則系統會自動分配
    length : 指定內存段的長度
    prot : 內存段的訪問權限,通過按位與或可以取以下幾種值

    flag : 選項
    fd : 被映射文件對應的文件描述符
    offset : 文件的偏移量
    返回值:
    成功:成功時返回指向內存區域的指針
    失敗:返回MAP_FAILED并設置errno


    splice
    splice函數用于在兩個文件描述符之間移動數據,而不需要數據在內核空間和用戶空間中來回拷貝

    需要注意的是,使用splice函數時fd_in和fd_out至少有一個是管道文件描述符,即

    #include <fcntl.h> ssize_t splice(int fd_in, loff_t *off_in, int fd_out,loff_t *off_out, size_t len, unsigned int flags);

    參數:
    out_fd : 待寫入內容的文件描述符
    off_out : 待寫入文件描述符的偏移量,如果文件描述符為管道則必須為空
    in_fd : 待讀出內容的文件描述符
    off_in : 待讀出文件描述符的偏移量,如果文件描述符為管道則必須為空
    len : 需要復制的字節數
    flags : 選項

    返回值:
    成功:返回在兩個文件描述符之間復制的字節數
    沒有數據:返回0
    失敗:返回-1并設置errno
    可能產生的errno


    tee
    tee函數用于在兩個管道文件描述符之間復制數據,并且它是直接復制,不會將數據讀出,所以源文件上的數據仍可以用于后面的讀操作

    #include <fcntl.h> ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);

    參數:
    out_fd : 待寫入內容的文件描述符
    in_fd : 待讀出內容的文件描述符
    len : 需要復制的字節數
    flags : 選項

    返回值:
    成功:返回在兩個文件描述符之間復制的字節數
    沒有數據:返回0
    失敗:返回-1并設置errno


    超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生

    總結

    以上是生活随笔為你收集整理的Linux网络编程 | 零拷贝 :sendfile、mmap、splice、tee的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。