提到高性能,我想大家都喜歡這個,今天我們就主要來弄明白在高性能的I/O設計中的幾個關鍵概念,做任何事最重要的第一步就是要把概念弄的清晰無誤不是麼?在這裡就是:阻塞,非阻塞,同步,異步。
OK, 現在來具體看看。
1. 阻塞和非阻塞是針對於進程在訪問數據的時候,根據IO操作的就緒狀態來采取的不同方式,說白了是一種讀取或者寫入操作函數的實現方式,阻塞方式下讀取或者寫入函數將一直等待,而非阻塞方式下,讀取或者寫入函數會立即返回一個狀態值。
2. 同步和異步是針對應用程序和內核的交互而言的,同步指的是用戶進程觸發IO操作並等待或者輪詢的去查看IO操作是否就緒,而異步是指用戶進程觸發IO操作以後便開始做自己的事情,而當IO操作已經完成的時候會得到IO完成的通知(異步的特點就是通知)。
所以一般I/O模型可以分為:同步阻塞,同步非阻塞,異步阻塞,異步非阻塞IO。
同步阻塞IO:
在此種方式下,用戶進程在發起一個IO操作以後,必須等待IO操作的完成,只有當真正完成了IO操作以後,用戶進程才能運行。JAVA傳統的IO模型屬於此種方式!
同步非阻塞IO:
在此種方式下,用戶進程發起一個IO操作以後邊可返回做其它事情,但是用戶進程需要時不時的詢問IO操作是否就緒,這就要求用戶進程不停的去詢問,從而引入不必要的CPU資源浪費。其中目前JAVA的NIO就屬於同步非阻塞IO。
異步阻塞IO:
此種方式下是指應用發起一個IO操作以後,不等待內核IO操作的完成,等內核完成IO操作以後會通知應用程序,這其實就是同步和異步最關鍵的區別,同步必須等待或者主動的去詢問IO是否完成,那麼為什麼說是阻塞的呢?因為此時是通過select系統調用來完成的,而select函數本身的實現方式是阻塞的,而采用select函數有個好處就是它可以同時監聽多個文件句柄(如果從UNP的角度看,select屬於同步操作。因為select之後,進程還需要讀寫數據),從而提高系統的並發性!
異步非阻塞IO:
在此種模式下,用戶進程只需要發起一個IO操作然後立即返回,等IO操作真正的完成以後,應用程序會得到IO操作完成的通知,此時用戶進程只需要對數據進行處理就好了,不需要進行實際的IO讀寫操作,因為真正的IO讀取或者寫入操作已經由內核完成了。目前Java中還沒有支持此種IO模型。
說到阻塞,首先得說說I/O等待。I/O等待是不可避免的,那麼既然有了等待,就會有阻塞,但是注意,我們說的阻塞是指當前發起I/O操作的進程被阻塞
同步阻塞I/O便是指,當進程調用某些涉及I/O操作的系統調用或庫函數時,比如accept()(注意accept也算在了i/o操作)、send()、recv()等,進程便暫停下來,等待I/O操作完成再繼續運行。這是一種簡單而有
效的I/O模型,它可以和多進程結合起來有效的利用CPU資源,但是代價就是多進程的大量內存開銷。
同步阻塞 進程坐水,就不能燒粥
同步非阻塞 類似於用一個進程坐水,燒粥. while(true){if... if... } 好處就是一個進程處理多個i/o請求. 劣勢就是需要不停的輪詢.
區別在於等不等待數據就緒. 因為數據占了等待的80%時間. 同步非阻塞的優勢就是一個進程裡同時處理多個I/O操作。
在同步阻塞I/O中,進程實際上等待的時間可能包括兩部分,一個是等待數據的就緒,另一個是等待數
據的復制,對於網絡I/O來說,前者的時間可能要更長一些。
與此不同的是,同步非阻塞I/O的調用不會等待數據的就緒,如果數據不可讀或者不可寫,它會立即返
回告訴進程。
比如我們使用非阻塞recv()接收網絡數據的時候,如果網卡緩沖區中沒有可接收的數據,函數就及時返回,告訴進程沒有數據可讀了。相比於阻塞I/O,這種非阻塞I/O結合反復的輪詢來嘗試
數據是否就緒,防止進程被阻塞,最大的好處便在於可以在一個進程裡同時處理多個I/O操作。但正是由於需要進程執行多次的輪詢來查看數據是否就緒,這花費了大量的CPU時間,使得進程處於忙碌等待狀態。
非阻塞I/O一般只針對網絡I/O有效,我們只要在socket的選項設置中使用O_NONBLOCK即可,這樣對於該socket的send()或recv()便采用非阻塞方式。
如果服務器想要同時接收多個TCP連接的數據,就必須輪流對每個socket調用接收數據的方法,比如recv()。不管這些socket有沒有可以接收的數據,都要詢問一遍,假如大部分socket並沒有數據可以接收,那麼進程便會浪費很多CPU時間用於檢查這些socket,這顯然不是我們所希望看到的。
同步和異步,阻塞和非阻塞,有些混用,其實它們完全不是一回事,而且它們修飾的對象也不相同。
阻塞和非阻塞是指當進程訪問的數據如果尚未就緒,進程是否需要等待,簡單說這相當於函數內部的實現區別,也就是未就緒時是直接返回還是等待就緒;
而同步和異步是指訪問數據的機制,同步一般指主動請求並等待I/O操作完畢的方式,當數據就緒後在讀寫的時候必須阻塞(區別就緒與讀寫二個階段,同步的讀寫必須阻塞),異步則指主動請求數據後便可以繼續處理其它任務,隨後等待I/O,操作完畢的通知,這可以使進程在數據讀寫時也不阻塞。(等待"通知")
多路就緒:1.強調多路. 2.只針對請求數據是否就緒.不針對i/o讀寫
epoll針對的是這樣的場景.
select, epoll都只需要進程被動接收到數據就緒"通知".符合異步的定義. 不需要一直在等(同步阻塞).或輪詢(同步非阻塞).
總結: 其實阻塞與非阻塞都可以理解為同步范疇下才有的概念,對於異步,就不會再去分阻塞非阻塞。對於用戶進程,接到異步通知後,就直接操作進程用戶態空間裡的數據好了。同步和異步是相對於應用和內核的交互方式而言的,同步需要主動去詢問,而異步的時候內核在IO事件發生的時候通知應用程序,而阻塞和非阻塞僅僅是系統在調用系統調用的時候函數的實現方式而已。
同步和異步是相對於應用和內核的交互方式而言的,同步需要主動去詢問,而異步的時候內核在IO事件發生的時候通知應用程序,而阻塞和非阻塞僅僅是系統在調用系統調用的時候函數的實現方式而已。