相信你們應該對「零拷貝」這個詞並不陌生了,這也算是大廠面試中的一個高頻考點,玩過 NETTY 的朋友應該對此至關熟悉,NETTY 的高併發很大程度上都是由於 NIO,而 NIO 的核心就是零拷貝技術了,今天這篇文章就讓你玩懂零拷貝。面試
傳統的IO模型是怎麼樣的?
咱們先來看一張圖,看看一個文件從磁盤傳輸到網卡究竟要經歷什麼樣的磨難:網絡
這種數據存儲的區域總體將它叫作非直接緩衝區。併發
能夠發現,竟然有四步數據拷貝的過程!!而且都是須要 CPU 去執行整個數據的傳輸過程的。異步
這時候就有個問題:這個過程也太繁瑣了,我就想傳輸一些數據,幹嗎要傳到用戶這裏,還要我本身再走一遍後續的流程,寫到 socket 緩衝區再發出去,你不能幫我實現嗎?socket
怎麼去優化傳統 IO 的流程?
再繼續看上面的流程圖理一下,看下能夠去掉哪些步驟ide
能夠看到在整個過程當中,從磁盤讀出來到發送給網卡的數據,文件內容都是不會發生改變的,可是真正要將文件傳輸到網卡得經歷4次文件內容的拷貝才行。函數
那麼以最簡單的的方式來講,可不能夠直接將磁盤中的數據傳輸到網卡呢?高併發
答案是固然不能夠,緣由很簡單,由於磁盤和網卡都是外部設備,因此必定須要有一箇中間的緩衝區域來取存儲數據,作一個轉發的做用。性能
那麼咱們再看看流程圖,能作緩衝的有兩個區域,一個是內核緩衝區,一個是socket緩衝區,用哪個?優化
這個問題應該很好選擇了:只有用內核緩衝區來作緩衝區。socket 確定不能夠了,由於socket 與我操做系統無瓜。
那麼可不能夠經過內核緩衝區直接給網卡發送數據呢?
看樣子是能夠的,咱們來看看,socket 緩衝區的做用是什麼?
socket緩衝區的做用每一個 socket 被建立後,都會分配兩個緩衝區,輸入緩衝區和輸出緩衝區。
write()/send() 並非當即向網絡中傳輸數據,而是先將數據寫入緩衝區中,而後再由 TCP 協議將數據從緩衝區發送到目標機器上。一旦將數據寫入到緩衝區,函數就能夠成功返回,無論它們它們什麼時候被髮送到網絡,也無論有沒有到達目標機器,這些都是 TCP 協議負責的事情。
因此socket就是用來傳輸網絡數據的,看來沒它還不行。
可是咱們換個思路,是否是說,只須要告訴 socket 要傳輸哪些數據就能夠了?而後文件內容就能夠直接用內核緩衝區的就行。
零拷貝(zero copy)是怎麼作到性能提高的當你能讀懂上面的內容,基本上就已經能摸到零拷貝的核心脈絡了,其實零拷貝就是使用內存映射來消除數據拷貝次數的,而後再使用 DMA 技術來減小CPU的工做時間。
就單從拷貝次數的性能來看,至少能夠將性能提升百分之五十以上。
DMADMA在上文中常常提到這個很重要的詞彙,它在整個零拷貝的流程當中佔比是很大的,能幫助 CPU 作大量的工做,接下來咱們介紹一下這個神奇的技術。
DMA就是直接存儲器訪問,DMA (Direct Memory Access,直接存儲器訪問) 是全部現代電腦的重要特點,它容許不一樣速度的硬件裝置來溝通,而不須要依賴於 CPU 的大量中斷負載。不然,CPU 須要歷來源把每一片斷的資料複製到暫存器,而後再次把它們寫回到新的地方。在這個時間中,CPU 對於其餘的工做來講就沒法使用。
原理:DMA 傳輸將數據從一個地址空間複製到另一個地址空間。當 CPU 初始化這個傳輸動做,傳輸動做自己是由 DMA 控制器來實行和完成。
零拷貝總體流程圖
認真看到這裏後,相信你應該對零拷貝已經有了深入的理解。那麼 NIO 究竟是幹什麼的?既然說了一篇文章讓你玩懂 零拷貝和NIO,那 NIO也必然不可少。
爲何須要 NIO ?
全部的系統I/O都分爲兩個階段:
須要注意的是:
等待就緒的阻塞是不使用CPU的,是在「空等」;而真正的讀寫操做的阻塞是使用CPU的,真正在」幹活」,並且這個過程很是快,屬於memory copy,帶寬一般在1GB/s級別以上,能夠將它理解爲基本不耗時。
咱們先來看看傳統IO是怎麼作的
在傳統的 socket IO中,須要爲每一個鏈接建立一個線程。
一個線程對應一個鏈接,只處理一個鏈接的事情,這就是傳統的socket IO。
當併發的鏈接數量很是巨大時,線程所佔用的棧內存和CPU線程切換的開銷就會很是大。
這種情境下還有可能會出現線程數量小於鏈接數量的狀況,因此每一個線程進行 I O操做時就不能阻塞,若是阻塞,有些鏈接便得不處處理。
如上圖,假如如今有三條線程在管理三條鏈接,而若是此時有第四個任務插入,那麼就只能等待前面任務執行完成。
它的操做就像是一條流水線同樣,是串行阻塞的,因此傳統 IO 咱們也稱爲 BIO。
傳統 IO 也不知道何時該處理數據,因此只能一直「傻等」。
而爲了解決這些問題,NIO 就出現了。
NIO 是 怎麼解決這些問題的?
咱們先來介紹一下 NIO 的核心組件:
通道有以下4個事件可供咱們監聽:
首先咱們須要註冊當這幾個事件到來的時候所對應的處理器。而後在合適的時機告訴事件選擇器:我對這個事件感興趣。
也就是說,在選擇器上註冊了這四個事件的處理器,用來處理 channel 的事件,當 channel 某個事件真的準備就緒了,能夠進行下一步的動做時,再告訴服務端來處理相應的數據,把相應的任務分配給服務端,這樣就能更好的利用 cpu 的資源。
前面咱們說的零拷貝,就是在這時數據處理時發生的。
NIO 和 IO 有什麼區別?從傳統 IO 模型 到 NIO 零拷貝模型咱們能夠看出,一個新技術的產生到崛起確定是由於它能知足以前技術知足不了的需求,或者性能有很高的提高。
傳統 IO 傳輸須要進行四次的數據內容拷貝,包括用戶態和內核態的切換,數據載體(磁盤、網卡)和內核態的切換,整個過程是阻塞的,浪費了不少資源。
而 NIO 是經過通道,選擇器等核心模塊,將整個 IO 處理過程變爲異步的方式,只有其數據任務真正就緒了,纔會讓 cpu 去作處理,大量的提升了性能,亦節省了資源。
零拷貝是使用了內存映射,作到了內核態和用戶態數據的零拷貝。而不是讓內核態和用戶態之間的數據再經過拷貝的方式傳輸。而由於使用了內存映射的關係,因此零拷貝技術沒法對數據內容作更改。
它的拷貝方式使用了 DMA 技術,目的就是爲了解決 CPU 拷貝數據的方式,讓拷貝數據再也不佔用 CPU 的資源,有 DMA 去完成。