常用網路I/O模型
I/O操作主要是由作業系統來完成的。根據UNIX的設計,共有5種類型的I/O模型。
·阻塞I/O。
·非阻塞I/O。
·I/O複用(select和poll)。
·訊號驅動I/O(SIGIO)。
·非同步I/O(Posix.1的aio_系列函式)。
上述模型或多或少地影響了其他作業系統的I/O模型設計。
阻塞I/O模型
阻塞I/O模型是指,當請求無法立即完成則保持阻塞狀態。主要分為以下兩個階段。
·階段1:等待資料就緒。網路I/O的情況就是等待遠端資料陸續抵達;磁碟I/O的情況就是等待磁碟資料從磁碟上讀取到核心態記憶體中。
·階段2:資料複製。出於系統安全,使用者態的程式沒有許可權直接讀取核心態記憶體,因此核心負責把核心態記憶體中的資料複製一份到使用者態記憶體中。
阻塞I/O模型如圖3-5所示。
圖3-5 阻塞I/O模型
本節中將recvfrom函式視為系統呼叫。一般recvfrom實現都有一個從應用程式程序執行到核心中執行的切換,再返回到應用程序的切換。
圖3-5中,程序阻塞的整段時間是指從呼叫recvfrom開始到它返回的這段時間,當程序返回成功指示時,應用程序開始處理資料報。
非阻塞I/O模型
非阻塞I/O模型處理流程如下。
·Socket設定為NONBLOCK(非阻塞)就是告訴核心,當所請求的I/O操作無法完成時,不要將程序“睡眠”,而是立刻返回一個錯誤碼(EWOULDBLOCK),這樣請求就不會阻塞。
·I/O操作函式將不斷地測試資料是否已經準備好,如果沒有準備好,繼續測試,直到資料準備好為止。整個I/O請求的過程中,雖然使用者執行緒每次發起I/O請求後可以立即返回,但是為了等到資料,仍需要不斷地輪詢、重複請求,這是對CPU時間的極大浪費。
·資料準備好了,從核心複製到使用者空間。
非阻塞I/O模型如圖3-6所示。
圖3-6 非阻塞I/O模型
一般很少直接使用這種模型,而是在其他I/O模型中使用非阻塞I/O這一特性。這種方式對單個I/O請求的意義不大,但給I/O複用“鋪平了道路”。
I/O複用模型
I/O複用會用到select或者poll函式,在這兩個函式的某一個上阻塞,而不是阻塞於真正的I/O系統呼叫。函式也會使程序阻塞,但是和阻塞I/O所不同的是,這兩個函式可以同時阻塞多個I/O操作。而且可以同時對多個讀操作、多個寫操作的I/O函式進行檢測,直到有資料可讀或可寫時,才真正呼叫I/O操作函式。
I/O複用模型如圖3-7所示。從流程上來看,使用select函式進行I/O請求和同步阻塞模型沒有太大的區別,甚至還多了監視Socket,以及呼叫select函式的額外操作,效率更差。但是,使用select最大的優勢是使用者可以在一個執行緒內同時處理多個Socket的I/O請求。使用者可以註冊多個Socket,然後不斷地呼叫select來讀取被啟用的Socket,即可達到在同一個執行緒內同時處理多個I/O請求的目的。而在同步阻塞模型中,必須透過多執行緒的方式才能達到這個目的。
I/O複用模型使用Reactor設計模式實現了這一機制。
圖3-7 I/O複用模型
呼叫select/poll函式由一個使用者態執行緒負責輪詢多個Socket,直到某個階段1的資料就緒,再通知實際的使用者態執行緒執行階段2的複製操作。透過一個專職的使用者態執行緒執行非阻塞I/O輪詢,模擬實現了階段1的非同步化。
在Java領域,著名的網路程式設計框架Netty就是採用了Reactor模型。
訊號驅動I/O模型
首先,我們允許Socket進行訊號驅動I/O,並透過呼叫sigaction來安裝一個訊號處理函式,程序繼續執行並不阻塞。當資料準備好時,程序會收到一個SIGIO訊號,可以在訊號處理函式中呼叫recvfrom來讀取資料報,並通知主迴圈資料已準備好被處理,也可以通知主迴圈,讓它來讀取資料報。
訊號驅動I/O(SIGIO)模型如圖3-8所示。
圖3-8 訊號驅動I/O(SIGIO)模型
該模型的優點是,當等待資料報到達時,可以不阻塞。主迴圈可以繼續執行,只是等待訊號處理程式的通知:或者資料已準備好被處理,或者資料報已準備好可讀。
非同步I/O模型
非同步I/O是POSIX規範定義的。通常,這些函式會通知核心來啟動操作並在整個操作(包括從核心複製資料到我們的快取中)完成時通知我們。
該模型與訊號驅動I/O(SIGIO)模型的不同點在於,驅動I/O(SIGIO)模型告訴我們I/O操作何時可以啟動,而非同步I/O模型告訴我們I/O操作何時完成。
呼叫aio_read函式,告訴核心傳遞描述字、快取區指標、快取區大小、檔案偏移,然後立即返回,我們的程序不阻塞直到I/O操作完成。
當核心將資料複製到快取區後,才會生成一個訊號,來通知應用程式。
非同步I/O模型如圖3-9所示。
圖3-9 非同步I/O模型
非同步I/O模型使用Proactor設計模式實現了這一機制。非同步I/O模型會告知核心,當整個過程(包括階段1和階段2)全部完成時,通知應用程式來讀資料。
幾種I/O模型的比較
前4種模型的區別是階段1不相同,階段2基本相同,都是將資料從核心複製到呼叫者的快取區。而非同步I/O的兩個階段都不同於前4個模型。5種I/O模型的比較如圖3-10所示。
圖3-10 5種I/O模型的比較
同步I/O操作引起請求程序阻塞,直到I/O操作完成。非同步I/O操作不引起請求程序阻塞。阻塞I/O模型、非阻塞I/O模型、I/O複用模型和訊號驅動I/O模型都是同步I/O模型,而非同步I/O模型才是真正的非同步I/O。