服務端處理網路請求的典型過程: 處理步驟包括: 獲取請求數據,客戶端與伺服器建立連接發出請求,伺服器接受請求(1 3)。 構建響應,當伺服器接收完請求,併在用戶空間處理客戶端的請求,直到構建響應完成(4)。 返回數據,伺服器將已構建好的響應再通過內核空間的網路 I/O 發還給客戶端(5 7)。 設計 ...
服務端處理網路請求的典型過程:
處理步驟包括:
獲取請求數據,客戶端與伺服器建立連接發出請求,伺服器接受請求(1-3)。
構建響應,當伺服器接收完請求,併在用戶空間處理客戶端的請求,直到構建響應完成(4)。
返回數據,伺服器將已構建好的響應再通過內核空間的網路 I/O 發還給客戶端(5-7)。
設計服務端併發模型時,主要有如下兩個關鍵點:
伺服器如何管理連接,獲取輸入數據。
伺服器如何處理請求。
阻塞調用與非阻塞調用 (阻塞、非阻塞的討論對象是調用者)
1.阻塞調用是指調用結果返回之前,當前線程會被掛起。調用線程只有在得到結果之後才會返回。
2.非阻塞調用指在不能立刻得到結果之前,該調用不會阻塞當前線程。
兩者的最大區別在於被調用方在收到請求到返回結果之前的這段時間內,調用方是否一直在等待。
阻塞是指調用方一直在等待而且別的事情什麼都不做;非阻塞是指調用方先去忙別的事情。
同步處理與非同步處理 (同步、非同步的討論對象是被調用者)
1.同步處理是指被調用方得到最終結果之後才返回給調用方。
2.非同步處理是指被調用方先返回應答,然後再計算調用結果,計算完最終結果後再通知並返回給調用方。
阻塞式 I/O 模型(blocking I/O)
在阻塞式 I/O 模型中,應用程式在從調用 recvfrom 開始到它返回有數據報準備好這段時間是阻塞的,recvfrom 返回成功後,應用進程開始處理數據報。
比喻:一個人在釣魚,當沒魚上鉤時,就坐在岸邊一直等。
優點:程式簡單,在阻塞等待數據期間進程/線程掛起,基本不會占用 CPU 資源。
缺點:每個連接需要獨立的進程/線程單獨處理,當併發請求量大時為了維護程式,記憶體、線程切換開銷較大,這種模型在實際生產中很少使用。
非阻塞式 I/O 模型(non-blocking I/O)
在非阻塞式 I/O 模型中,應用程式把一個套介面設置為非阻塞,就是告訴內核,當所請求的 I/O 操作無法完成時,不要將進程睡眠。而是返回一個錯誤,應用程式基於 I/O 操作函數將不斷的輪詢數據是否已經準備好,如果沒有準備好,繼續輪詢,直到數據準備好為止。
比喻:邊釣魚邊玩手機,隔會再看看有沒有魚上鉤,有的話就迅速拉桿。
優點:不會阻塞在內核的等待數據過程,每次發起的 I/O 請求可以立即返回,不用阻塞等待,實時性較好。
缺點:輪詢將會不斷地詢問內核,這將占用大量的 CPU 時間,系統資源利用率較低,所以一般 Web 伺服器不使用這種 I/O 模型。
I/O 復用模型(I/O multiplexing)
在 I/O 復用模型中,會用到 Select 或 Poll 函數或 Epoll 函數(Linux 2.6 以後的內核開始支持),這兩個函數也會使進程阻塞,但是和阻塞 I/O 有所不同。
這兩個函數可以同時阻塞多個 I/O 操作,而且可以同時對多個讀操作,多個寫操作的 I/O 函數進行檢測,直到有數據可讀或可寫時,才真正調用 I/O 操作函數。
比喻:放了一堆魚竿,在岸邊一直守著這堆魚竿,沒魚上鉤就玩手機。
優點:可以基於一個阻塞對象,同時在多個描述符上等待就緒,而不是使用多個線程(每個文件描述符一個線程),這樣可以大大節省系統資源。
缺點:當連接數較少時效率相比多線程+阻塞 I/O 模型效率較低,可能延遲更大,因為單個連接處理需要 2 次系統調用,占用時間會有增加。
信號驅動式 I/O 模型(signal-driven I/O)
在信號驅動式 I/O 模型中,應用程式使用套介面進行信號驅動 I/O,並安裝一個信號處理函數,進程繼續運行並不阻塞。
當數據準備好時,進程會收到一個 SIGIO 信號,可以在信號處理函數中調用 I/O 操作函數處理數據。
比喻:魚竿上系了個鈴鐺,當鈴鐺響,就知道魚上鉤,然後可以專心玩手機。
優點:線程並沒有在等待數據時被阻塞,可以提高資源的利用率。
缺點:信號 I/O 在大量 IO 操作時可能會因為信號隊列溢出導致沒法通知。
信號驅動 I/O 儘管對於處理 UDP 套接字來說有用,即這種信號通知意味著到達一個數據報,或者返回一個非同步錯誤。
但是,對於 TCP 而言,信號驅動的 I/O 方式近乎無用,因為導致這種通知的條件為數眾多,每一個來進行判別會消耗很大資源,與前幾種方式相比優勢盡失。
非同步 I/O 模型(asynchronous I/O)
由 POSIX 規範定義,應用程式告知內核啟動某個操作,並讓內核在整個操作(包括將數據從內核拷貝到應用程式的緩衝區)完成後通知應用程式。
這種模型與信號驅動模型的主要區別在於:信號驅動 I/O 是由內核通知應用程式何時啟動一個 I/O 操作,而非同步 I/O 模型是由內核通知應用程式 I/O 操作何時完成。
優點:非同步 I/O 能夠充分利用 DMA 特性,讓 I/O 操作與計算重疊。
缺點:要實現真正的非同步 I/O,操作系統需要做大量的工作。目前 Windows 下通過 IOCP 實現了真正的非同步 I/O。
而在 Linux 系統下,Linux 2.6才引入,目前 AIO 並不完善,因此在 Linux 下實現高併發網路編程時都是以 IO 復用模型模式為主。