網絡應用須要處理的無非就是兩大類問題,網絡I/O,數據計算。相對於後者,網絡I/O的延遲,給應用帶來的性能瓶頸大於後者。網絡I/O的模型大體有以下幾種:linux
網絡I/O的本質是socket的讀取,socket在linux系統被抽象爲流,I/O能夠理解爲對流的操做。這個操做又分爲兩個階段:編程
對於socket流而已,第一步一般涉及等待網絡上的數據分組到達,而後被複制到內核的某個緩衝區。第二步把數據從內核緩衝區複製到應用進程緩衝區。服務器
阻塞I/O是最流行的I/O模型。它符合人們最多見的思考邏輯。阻塞就是進程 "被" 休息, CPU處理其它進程去了(應用程序阻塞,CPU會經過調用add_wait_queue進入等待隊列,以後調用schedule調度到其餘進程。)。在網絡I/O的時候,進程發起recvform
系統調用,而後進程就被阻塞了,什麼也不幹,直到數據準備好,而且將數據從內核複製到用戶進程,最後進程再處理數據,在等待數據處處理數據的兩個階段,整個進程都被阻塞。不能處理別的網絡I/O。大體以下圖:網絡
這就比如咱們去釣魚,拋竿以後就一直在岸邊等,直到等待魚上鉤。而後再一次拋竿,等待下一條魚上鉤,等待的時候,什麼事情也不作,大概會胡思亂想吧。異步
在網絡I/O時候,非阻塞I/O也會進行recvform系統調用,檢查數據是否準備好,與阻塞I/O不同,"非阻塞將大的整片時間的阻塞分紅N多的小的阻塞, 因此進程不斷地有機會 '被' CPU光顧"。socket
也就是說非阻塞的recvform系統調用調用以後,進程並無被阻塞,內核立刻返回給進程,若是數據還沒準備好,此時會返回一個error。進程在返回以後,能夠乾點別的事情,而後再發起recvform系統調用。重複上面的過程,循環往復的進行recvform系統調用。這個過程一般被稱之爲輪詢
。輪詢檢查內核數據,直到數據準備好,再拷貝數據到進程,進行數據處理。須要注意,拷貝數據整個過程,進程仍然是屬於阻塞的狀態。async
咱們再用釣魚的方式來類別,當咱們拋竿入水以後,就看下魚漂是否有動靜,若是沒有魚上鉤,就去幹點別的事情,好比再挖幾條蚯蚓。而後不久又來看看魚漂是否有魚上鉤。這樣往返的檢查又離開,直到魚上鉤,再進行處理。分佈式
能夠看出,因爲非阻塞的調用,輪詢佔據了很大一部分過程,輪詢會消耗大量的CPU時間。結合前面兩種模式。若是輪詢不是進程的用戶態,而是有人幫忙就行了。多路複用正好處理這樣的問題。性能
多路複用有兩個特別的系統調用select
或poll
。select調用是內核級別的,select輪詢相對非阻塞的輪詢的區別在於---前者能夠等待多個socket,當其中任何一個socket的數據準好了,就能返回進行可讀,而後進程再進行recvform系統調用,將數據由內核拷貝到用戶進程,固然這個過程是阻塞的。多路複用有兩種阻塞,select或poll調用以後,會阻塞進程,與第一種阻塞不一樣在於,此時的select不是等到socket數據所有到達再處理, 而是有了一部分數據就會調用用戶進程來處理,也能夠理解爲"非阻塞"吧。學習
對於多路複用,也就是輪詢多個socket。釣魚的時候,咱們僱了一個幫手,他能夠同時拋下多個釣魚竿,任何一杆的魚一上鉤,他就會拉桿。他只負責幫咱們釣魚,並不會幫咱們處理,因此咱們還得在一幫等着,等他把收杆。咱們再處理魚。多路複用既然能夠處理多個I/O,也就帶來了新的問題,多個I/O之間的順序變得不肯定了,固然也能夠針對不一樣的編號。
瞭解了前面三種模式,在用戶進程進行系統調用的時候,他們在等待數據到來的時候,處理的方式不同,直接等待,輪詢,select或poll輪詢,第一個過程有的阻塞,有的不阻塞,有的能夠阻塞又能夠不阻塞。當時第二個過程都是阻塞的。從整個I/O過程來看,他們都是順序執行的,所以能夠歸爲同步模型(asynchronous)。都是進程主動向內核檢查。
相對於同步I/O,異步I/O不是順序執行。用戶進程進行aio_read
系統調用以後,不管內核數據是否準備好,都會直接返回給用戶進程,而後用戶態進程能夠去作別的事情。等到socket數據準備好了,內核直接複製數據給進程,而後從內核向進程發送通知。I/O兩個階段,進程都是非阻塞的。
比以前的釣魚方式不同,這一次咱們僱了一個釣魚高手。他不只會釣魚,還會在魚上鉤以後給咱們發短信,通知咱們魚已經準備好了。咱們只要委託他去拋竿,而後就能跑去幹別的事情了,直到他的短信。咱們再回來處理已經上岸的魚。
經過對上述幾種模型的討論,須要區分阻塞和非阻塞,同步和異步。他們實際上是兩組概念。區別前一組比較容易,後一種每每容易和前面混合。在我看來,所謂同步就是在整個I/O過程。尤爲是拷貝數據的過程是阻塞進程的,而且都是應用進程態去檢查內核態。而異步則是整個過程I/O過程用戶進程都是非阻塞的,而且當拷貝數據的時是由內核發送通知給用戶進程。
對於同步模型,主要是第一階段處理方法不同。而異步模型,兩個階段都不同。這裏咱們忽略了信號驅動模式。這幾個名詞仍是容易讓人迷惑,只有同步模型才考慮阻塞和非阻塞,由於異步確定是非阻塞,異步非阻塞的說法感受多此一舉。
本文所討論的IO模型來自大名鼎鼎的《unix網絡編程:卷1套接字聯網API》。單臺服務器中的linux系統。分佈式的環境或許會不同。我的學習筆記,參考了網絡上大多數文章,作了一點小測試。