Linux 百萬併發 零拷貝 實現原理

2021-10-18 17:07:59 字數 2503 閱讀 9564

使用者程序通過系統呼叫mmap函式進入核心態,發生第1次上下文切換,並建立核心緩衝區;

發生缺頁中斷,cpu通知dma讀取資料;

dma拷貝資料到物理記憶體,並建立核心緩衝區和物理記憶體的對映關係;

建立使用者空間的程序緩衝區和同一塊物理記憶體的對映關係,由核心態轉變為使用者態,發生第2次上下文切換;

使用者程序進行邏輯處理後,通過系統呼叫socket send,使用者態進入核心態,發生第3次上下文切換;

系統呼叫send建立網路緩衝區,並拷貝核心讀緩衝區資料;

dma控制器將網路緩衝區的資料傳送網絡卡,並返回,由核心態進入使用者態,發生第4次上下文切換;

總結避免了核心空間和使用者空間的2次cpu拷貝,但增加了1次核心空間的cpu拷貝,整體上相當於只減少了1次cpu拷貝;

針對大檔案比較適合mmap,小檔案則會造成較多的記憶體碎片,得不償失;

當mmap乙個檔案時,如果檔案被另乙個程序截獲可能會因為非法訪問導致程序被sigbus 訊號終止;

sendfilesendfile是在linux2.1引入的,它只需要2次上下文切換和1次核心cpu拷貝、2次dma拷貝,函式原型ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

out_fd為檔案描述符,in_fd為網路緩衝區描述符,offset偏移量(預設null),count檔案大小。

它的內部執行流程是這樣的

使用者程序系統呼叫senfile,由使用者態進入核心態,發生第1次上下文切換;

cpu通知dma控制器把檔案資料拷貝到核心緩衝區;

核心空間自動呼叫網路傳送功能並拷貝資料到網路緩衝區;

cpu通知dma控制器傳送資料;

sendfile系統呼叫結束並返回,程序由核心態進入使用者態,發生第2次上下文切換;

總結資料處理完全是由核心操作,減少了2次上下文切換,整個過程2次上下文切換、1次cpu拷貝,2次dma拷貝;

雖然可以設定偏移量,但不能對資料進行任何的修改;

sendfile+dma gatherlinux2.4對sendfile進行了優化,為dma控制器引入了gather功能,就是在不拷貝資料到網路緩衝區,而是將待傳送資料的記憶體位址和偏移量等描述資訊存在網路緩衝區,dma根據描述資訊從核心的讀緩衝區擷取資料並傳送。它的流程是如下

使用者程序系統呼叫senfile,由使用者態進入核心態,發生第1次上下文切換;

cpu通知dma控制器把檔案資料拷貝到核心緩衝區;

cpu通知dma控制器,dma根據網路緩衝區中的資料描述擷取資料並傳送;

sendfile系統呼叫結束並返回,程序由核心態進入使用者態,發生第2次上下文切換;

總結需要硬體支援,如dma;

整個過程2次上下文切換,0次cpu拷貝,2次dma拷貝,實現真正意義上的零拷貝;

依然不能修改資料;

但那時的sendfile有個致命的缺陷,如果你檢視sendfild手冊,你會發現如下描述

in_fd不僅僅不能是socket,而且在2.6.33之前sendfile的out_fd必須是socket,因此sendfile幾乎成了專為網路傳輸而設計的,限制了其使用範圍比較狹窄。2.6.33之後out_fd才可以是任何file,於是乎出現了splice。

splice鑑於sendfile的缺點,在linux2.6.17中引入了splice,它在讀緩衝區和網路操作緩衝區之間建立管道避免cpu拷貝:先將檔案讀入到核心緩衝區,然後再與核心網路緩衝區建立管道。它的函式原型ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);

它的執行流程如下

使用者程序系統呼叫splice,由使用者態進入核心態,發生第1次上下文切換;

cpu通知dma控制器把檔案資料拷貝到核心緩衝區;

建立核心緩衝區和網路緩衝區的管道;

cpu通知dma控制器,dma從管道讀取資料並傳送;

splice系統呼叫結束並返回,程序由核心態進入使用者態,發生第2次上下文切換;

總結整個過程2次上下文切換,0次cpu拷貝,2次dma拷貝,實現真正意義上的零拷貝;

依然不能修改資料;

fd_in和fd_out必須有乙個是管道;

teetee與splice類同,但fd_in和fd_out都必須是管道。寫在最後

零拷貝實現原理

從 作業系統中直接操作記憶體,netty底層就是零拷貝,netty就是具有零拷貝功能 netty的零拷貝主要體現在三個方面 1 netty的接收和傳送bytebuffer採用direct buffers,使用堆外直接記憶體進行scoket讀寫,不需要進行位元組緩衝區的二次拷貝.如果使用傳統的堆記憶體...

sendfile 實現零拷貝詳解

2013年11月18日 11 17 供稿中心 網際網路運營部 摘要 linux的sendfile 系統呼叫 伺服器響應乙個http請求的步驟如下 1 把磁碟檔案讀入核心緩衝區 2 從核心緩衝區讀到記憶體 3 處理 靜態資源不需處理 4 傳送到網絡卡的核心緩衝區 傳送快取 5 網絡卡傳送資料 資料從第...

原 VC SOCKET實現多執行緒併發連線

最近想弄乙個可以實現多執行緒併發連線的程式,用vc實現,可是網上沒有現成的多執行緒併發原始碼,我只好自己做乙個。我開始一直不知道怎麼弄,後來在看乙個帖子的時候,有一句話提醒了我 accept函式會返回乙個新的socket連線。大意是這樣哈 說到這你可能已經會了。靈感就在一瞬間啊!我以前怎麼沒有注意到...