週報P.02 - Zero Copy
🚀 三句話總結
- 熟悉硬體與程式的搭配和防護方案
- 熟悉IO在記憶體和硬碟中的操作
- 了解上述兩點後,則可以使用新設備優化IO效率
☘️ 為什麼我要了解
因為在資料傳輸時一定會經過通訊模組,而為了實現通訊模組的每一層都有優化方案,所以我們必須了解他,達成一個閉環,而實體曾理論上軟體是無法優化,但ZeroCopy是少數可以由軟體控制硬體的優化方案。
🎨 改變了什麼
能夠解釋所有硬體和軟體的配合方案,能夠實現軟體如何運作在硬體的方案,有種學完後會茅塞頓開,所有東西都是靠這個方案連結,不然之前學的東西都很零散聽起來都很有道理,但合在一起就沒道理,而這東西讓所有東西合在一起變得有道理。
✍️ 總結和心得
計算機架構設計方法
在講述Zero Copy之前先簡單介紹下計算機的架構,眾所皆知計算機架構大致長這樣
這種基礎五大單元組成,但這是很抽象的講法,所以今天講一下具體的講法
分級保護域
分級保護域是一種提升計算機安全的設計方法,也被廣泛用於各種計算機中,像是x86,而只要採用這種方法設計就能提高安全性,詳細請看以下圖片
在上圖總共有4層,規範了每一層是什麼
- Ring 0 : 核心層
- Ring 1 2 : I/O 和外部硬體裝置
- Ring 3 : 應用程式
今天作業系統和所有程式,像是chrome、win10、小算盤 ..etc這些軟體,在計算機中全部劃分為第三環,也就是用戶層,當我們在執行這些軟體時,CPU內的狀態會記錄當前CPU處在第三層,也就是用戶態
而當這些程式需要 讀取硬碟資料 or 攝像機 or 喇叭 …etc 都會切換狀態,0~2層 我們稱為核心態
這邊有個規則,高層數的Ring不能直接操作低層數的Ring,像是Ring3的程式不能直接操作Ring 2的功能,這之間具有非常強力的隔離規則,而低層數的Ring可以直接呼叫高層數Ring,像是Ring0就擁有了至高的權力,可以直接呼記所有層數的功能。
所以嚴格來說所有軟體都在用戶層,而像是chrome這軟體,會需要能夠收發網路封包才能顯示東西在畫面上,但是根據上述規則高層數不能呼叫到低層數,而網卡則在核心層,那麼勢必有個接口讓我們操作網卡,不然所有軟體都沒辦法呼叫到硬體,而這接口叫做系統調用(syscall),有些程式語言會包裝 系統調用接口,像是
1 | read() |
read 這函數就封裝了系統調用程式。而一隻程式就有一個PID,也就是進程,該進程也就對應了一個CPU狀態
PS:分層等級與作業系統中的「系統管理員」毫無關係,那怕是系統管理員,依然是應用程式,依然是Ring 3
基礎的數據操作過程
在理解用戶態和核心態後,我們就來理解數據的操作過程,在電腦執行操作時,無外乎就是在操作數據,而數據通常存放在Disk當中,這東西是外部裝置,所以勢必要轉換為核心態,而當我們在程式中執行讀出,就會依照以下流程執行:
- 應用程式呼叫Read功能,而Read功能封裝了 系統調用 ,狀態切換為核心態
- CPU 向 硬碟發出I/O請求,磁碟收到後就準備資料
- 磁碟準備好後,向CPU 發出I/O中斷,報告我硬碟已經準備好了
- CPU收到I/O中斷後,開始拷貝數據 從 硬碟 to 內核緩存
- CPU Copy完後,再拷貝數據從 內核緩存 to 應用緩存
- CPU Copy完後,轉換為用戶態,並返回 read()
但是這架構其實還可以優化,畢竟這些雜事沒必要叫CPU負責,為了釋放更多CPU效能,像是 CPU 要拷貝 從 硬碟 to 內核緩存這條路,或許我們可以新增一個設備讓CPU只用負責 Copy 從 內核 to 應用程式,該設備我們稱為DMA
DMA (Direct Memory Access)
DMA可以說是CPU的小弟,他是一個獨立的硬體裝置
可以取代CPU Copy 從 硬碟 to 內核緩存的工作,好讓CPU可以去做其他的事情,等到DMA Copy完成後,CPU在將其複製到用戶緩存區
而如果用戶層是一個靜態網站,會read靜態資源,然後write to Socket緩存,所以綜上所述:
讀的過程會有2次狀態改變、1次DMA拷貝、1次CPU拷貝
寫的過程會有2次狀態改變、1次DMA拷貝、1次CPU拷貝
讀寫過程會有4次狀態改變、2次DMA拷貝、2次CPU拷貝
什麼是Zero Copy
在當今的網路服務中,都是基於Clinet to Server,而Client的資源我們通常都是靜態資源,靜態資源的特性就是Server端只用拿檔案然後無修改直接透過網卡傳給Client,這就是靜態的特性。
那麼這種情況下,在Server中的用戶層需要拿到東西嗎?
我們難道不能直接把資料Copy到網卡緩存嗎?
所以ZeroCopy 就是減少 狀態改變的次數和CPU拷貝的次數
常見的有以下方式實現取代:
- mmap+write
- sendfile
- sendfile+DMA
- splice
mmap+write
該方法可以取代 read+write方式,並且減少 1次CPU拷貝次數。
mmap是Linux提供的一種虛擬內存映射同一個物理地址的方法,也就是 CPU不必Copy 從 內核緩存 to 應用緩存,由於映射所以就可以共享資料
所以綜上所述:
讀的過程會有2次狀態改變、1次DMA拷貝、0次CPU拷貝
寫的過程會有2次狀態改變、1次DMA拷貝、1次CPU拷貝
讀寫過程會有4次狀態改變、2次DMA拷貝、1次CPU拷貝
優缺點:
- 優點:操作大資料時會有顯著優勢,因為不用複製一次,就可以讓用戶層操作到資料
- 優點:無須硬體配合就能使用 (DMA只是輔助不是一定)
- 缺點1:操作小資料時會有浪費的問題,因為內存映射資訊的最小單位是4KB,如果你的檔案連4K都不到,就會變成 資料映射資訊比資料還大。
- 缺點2:mmap適用於大文件,而如果頻繁使用,會對CPU內核緩存挑戰極大
sendfile
在linux 2.1版本新增該函數,該函數可以取代 read+write方式,並且減少 2次CPU拷貝次數。
sendfile函數如其名,只能作用在傳送檔案,非常適合靜態資源,因為sendfile根本不打算read時候把資源複製給用戶層,因為sendfile就預期你只是讀硬碟資料然後不做修改直接傳送到網卡給別人,那麼就沒必要給用戶層,直接呼叫CPU Copy 從 內核緩存 to Socket緩存,再使用DMA Copy 從 Socket緩存 to 網卡,之後進行發送資料
所以綜上所述:
讀的過程會有1次狀態改變、1次DMA拷貝、0次CPU拷貝
寫的過程會有1次狀態改變、1次DMA拷貝、1次CPU拷貝
讀寫過程會有2次狀態改變、2次DMA拷貝、1次CPU拷貝
優缺點:
- 優點:減少了兩次的狀態改變,比mmap+write相比優化了很多
- 優點:無須硬體配合就能使用 (DMA只是輔助不是一定)
- 缺點:因為無須Copy到應用層就能傳送檔案,所以應用程式根本拿不到資料,算是一個很強大的限制性,比較適合靜態資源類型的Server應用程式(Nginx)。
sendfile+DMA
在linux 2.4版本,修改了sendfile函數,為DMA拷貝導入了 gather 操作,讓內核緩存可以儲存地址等資訊記錄到socket 緩存(類似虛擬緩存的映射也就是map),然後DMA可以根據儲存地址,呼叫 DMA Copy 從 內核緩存 to 網卡,這樣就無需將內核緩存複製到Socket緩存內,CPU就減少一次拷貝。
所以綜上所述:
讀的過程會有1次狀態改變、1次DMA拷貝、0次CPU拷貝
寫的過程會有1次狀態改變、1次DMA拷貝、0次CPU拷貝
讀寫過程會有2次狀態改變、2次DMA拷貝、0次CPU拷貝
優缺點:
- 優點:減少CPU的拷貝,讓CPU不用負責「拷貝」這簡單的任務
- 缺點:須要硬體配合就能使用,DMA在其中擔任核心角色
- 缺點:跟sendfile一樣的問題,應用程式拿不到資料就會發送資料給別人。
splice
在linux 2.6.17 版本新增該函數,該函數可以取代 read+write方式,並且無須硬體支援。
splice該函數可以建立一條管道,讓內核緩存 和 soket緩存之間形成一條通道,就可以避免CPU Copy的行為。
所以綜上所述:
讀的過程會有1次狀態改變、1次DMA拷貝、0次CPU拷貝
寫的過程會有1次狀態改變、1次DMA拷貝、0次CPU拷貝
讀寫過程會有2次狀態改變、2次DMA拷貝、0次CPU拷貝
優缺點:
- 優點:減少CPU的拷貝,讓CPU不用負責「拷貝」這簡單的任務
- 缺點:跟sendfile一樣的問題,應用程式拿不到資料就會發送資料給別人。