傳統的文件拷貝
傳統的讀取文件數據並發送到網絡的步驟如下:
(2)應用程序將數據從內核空間讀入用戶空間緩沖區;
(3)應用程序將讀到數據寫回內核空間並放入socket緩沖區;
(4)操作系統將數據從socket緩沖區復制到網卡接口,此時數據才能通過網絡發送。
用戶態與內核態
1.CPU狀態
內核態(Kernel Mode):運行操作系統程序,操作硬件
用戶態(User Mode):運行用戶程序
2.指令划分
特權指令:只能由操作系統使用、用戶程序不能使用的指令。 舉例:啟動I/O 內存清零 修改程序狀態字 設置時鍾 允許/禁止終端 停機
非特權指令:用戶程序可以使用的指令。 舉例:控制轉移 算數運算 取數指令 訪管指令(使用戶程序從用戶態陷入內核態)
3.特權級別
特權環:R0、R1、R2和R3
R0相當於內核態,R3相當於用戶態;不同級別能夠運行不同的指令集合。
4.CPU狀態之間的轉換
用戶態--->內核態:唯一途徑是通過中斷、異常、陷入機制(訪管指令)
內核態--->用戶態:設置程序狀態字PSW
5.內核態與用戶態的區別
內核態與用戶態是操作系統的兩種運行級別,當程序運行在3級特權級上時,就可以稱之為運行在用戶態。因為這是最低特權級,是普通的用戶進程運行的特權級,大部分用戶直接面對的程序都是運行在用戶態;當程序運行在0級特權級上時,就可以稱之為運行在內核態。
運行在用戶態下的程序不能直接訪問操作系統內核數據結構和程序。當我們在系統中執行一個程序時,大部分時間是運行在用戶態下的,在其需要操作系統幫助完成某些它沒有權力和能力完成的工作時就會切換到內核態(比如操作硬件)。
這兩種狀態的主要差別是:
- 處於用戶態執行時,進程所能訪問的內存空間和對象受到限制,其所處於占有的處理器是可被搶占的
- 處於內核態執行時,則能訪問所有的內存空間和對象,且所占有的處理器是不允許被搶占的。
6.用戶態到內核態的切換
系統調用:
這是用戶態進程主動要求切換到內核態的一種方式,用戶態進程通過系統調用申請使用操作系統提供的服務程序完成工作。比如fork()實際上就是執行了一個創建新進程的系統調用。而系統調用的機制其核心還是使用了操作系統為用戶特別開放的一個中斷來實現,例如Linux的int 80h中斷。
用戶程序通常調用庫函數,由庫函數再調用系統調用,因此有的庫函數會使用戶程序進入內核態(只要庫函數中某處調用了系統調用),有的則不會。
異常:
當CPU在執行運行在用戶態下的程序時,發生了某些事先不可知的異常,這時會觸發由當前運行進程切換到處理此異常的內核相關程序中,也就轉到了內核態,比如缺頁異常。
外圍設備的中斷:
當外圍設備完成用戶請求的操作后,會向CPU發出相應的中斷信號,這時CPU會暫停執行下一條即將要執行的指令轉而去執行與中斷信號對應的處理程序,如果先前執行的指令是用戶態下的程序,那么這個轉換的過程自然也就發生了由用戶態到內核態的切換。比如硬盤讀寫操作完成,系統會切換到硬盤讀寫的中斷處理程序中執行后續操作等。
Kafka 零拷貝
Linux 2.1版本內核引入了sendfile函數,用於將文件通過socket傳送。“零拷貝技術”只用將磁盤文件的數據復制到頁面緩存中一次,然后將數據從頁面緩存直接發送到網絡中(發送給不同的訂閱者時,都可以使用同一個頁面緩存),避免了重復復制操作。減少了內核態與用戶態的內存拷貝和進程切換
如果有10個消費者,傳統方式下,數據復制次數為4*10=40次,而使用“零拷貝技術”只需要1+10=11次,一次為從磁盤復制到頁面緩存,10次表示10個消費者各自讀取一次頁面緩存。