網絡IO的模式包括同步IO,異步IO,阻塞IO,非阻塞IO;這些IO模式之間的區別和聯繫是任何一個搞網絡編程(Socket編程)的人必須弄清楚的問題,下面我們就來一層層的揭開他們的面紗吧。在Unix網絡編程第一卷第六章中討論了五種IO模型:
1. 阻塞IO(Blocking IO)
2. 非阻塞IO(Nonblocking IO)
3. IO多路複用(IO multiplexing)
4. Signal Driven IO(實際中一般不使用)
5. 異步IO(Asynchronous IO)
由於第4種在實際中一般不使用,所以接下來在本文中我們就討論其餘4種模型;在討論模型之前,我們首先要明確IO過程中兩個非常重要的步驟(所有的IO模型都是用戶進程和內核在這兩個步驟上交互模式不同):
1. 內核等待數據準備(內核等待協議棧中運輸層接收緩衝區或發送緩衝區就緒)
2. 內核將緩衝區中的數據拷貝用戶態進程的地址空間中
Blocking IO
在Linux中,默認的Socket通信模式就是阻塞IO,該模式的過程如下圖所示:
從上圖可以看出,當用戶進程開始調用recvfrom的時候,內核就開始等待數據,當數據準備好之後,內核立即將數據從內核緩存中拷貝到進程的地址空間中,整個過程完成之後,recvfrom調用返回;在此之間,用戶進程被阻塞。
Nonblocking IO
當用戶希望以非阻塞的方式進行IO通信時,可以設置Socket爲非阻塞,非阻塞的模式圖如下:
當用戶進程調用recvfrom時,假如此時內核正在等待數據,則該調用會立即返回錯誤;假如此時數據已經準備好了,則內核會立即將數據拷貝到用戶進程的地址空間中併成功返回;上面圖示的是應用進程在不斷的輪詢,直到內核中數據準備好併成功返回爲止。
IO multiplexing
IO多路複用也被稱爲事件驅動IO(Event Driven IO),我們常用的select, poll, epoll等系統調用就是這種模型;這種模型的優點是可以同時處理多個socket連接。其基本原理是select/epoll系統調用會不斷輪詢所負責的所有socket連接,一旦某個socket的數據已經準備好,select便立即返回;然後用戶進程調用recvfrom觸發內核將該socket對應的數據從內核空間拷貝到應用進程的地址空間中併成功返回。
在IO multiplexing Model中,socket一般都被設置成non-blocking,但是,如上圖所示,內核在等待數據就緒的時候,用戶進程其實是block的,只不過進程是被select這個函數block,而不是被socket IO給block。
該模型用於服務器中對多個網絡併發連接進行處理會非常有效,分佈式緩存Memcached的服務端及常用的Web服務器就採用這種模型,Libevent就是基於這個模型實現的庫。
異步IO
Linux中異步IO其實用的非常少,它的模型如下圖所示:
從圖中我們可以看出,當用戶進程發起IO請求時,如果內核數據沒有準備好,則會立刻返回;返回後用戶進程繼續做之後的事情,而內核則等待數據就緒並將數據從內核拷貝到用戶空間,同時發送aio_read中指定的信號給用戶進程,告訴它數據已經成功發送給它。
下面我們來看看阻塞和非阻塞、同步和異步的內涵及區別:
阻塞、非阻塞是指當調用一個函數時,他是否能夠立即返回;
同步、異步是指進程間通信是否經過協調,是進程間通信的模型;比如進程A需要等待進程B執行完某件事情之後才能繼續執行,這就是同步;假如進程A的執行和進程B的執行好不相干,這就是異步;
因此,我們可以說阻塞調用或非阻塞調用,但不能說同步調用及異步調用,只能說同步通信或異步通信