1. 程式人生 > >深入瞭解幾種IO模型(阻塞非阻塞,同步非同步)

深入瞭解幾種IO模型(阻塞非阻塞,同步非同步)

一般來說,Linux下系統IO主要就是通過以下幾個函式open(),close(),read(),write(),send(),recv(),lseek(),今天就以recv()為例來介紹下IO模型中的同步非同步,阻塞非阻塞的區別。

先說阻塞與非阻塞的區別,recv()函式預設是阻塞的,什麼是阻塞呢?就是當你呼叫recv()函式時,整個程序或者執行緒就等待在這裡了,直到你recv的fd的所有資訊都被send過來,這麼做好處就是保證所有資訊都能夠完整的讀取了,但劣勢也很明顯,就是在recv()的過程中你的程序或執行緒做不了其它事情,由此,引入了非阻塞IO。

非阻塞IO是什麼呢,還是以recv()函式為例,當你將其設定為非阻塞時,每次當你recv()時,就直接返回,不管資訊有沒有完全send進來,好處很明顯,recv()了之後程序馬上能處理下一行程式碼,壞處也很明顯,就是你不知道你的訊息是否讀完了,這種問題就是TCP中大名鼎鼎的半包問題(解決辦法主要是通過一個buffer快取所有讀進來的訊息)

打一個比方,你要去開水房接3/4杯水,在阻塞情況下,你需要等待你的杯子接夠量,你才可以離開,當水是斷斷續續的時候,你就浪費了很多時間,因為在沒水的這段時間你不能做其它事情,好處就是所有水接完就是剛剛好的3/4杯,在非阻塞情況下,你每次接水就接一些,只要沒水了你就可以離開去做其它的事情,但需要你自己去看好量是不是夠了3/4杯。阻塞和非阻塞的I/O模型圖示如下:

阻塞IO模型:

                     

非阻塞IO模型:

                      

下面再說同步與非同步的區別,在POSIX定義中把同步IO操作定義為導致程序阻塞直到IO完成的操作,反之則是非同步IO,看概念感覺非同步跟非阻塞好像也沒有什麼區別,要好好理解

同步和非同步,就要詳細說明下IO過程:

IO過程主要分兩個階段:

1.資料準備階段

2.核心空間複製回用戶程序緩衝區空間

無論阻塞式IO還是非阻塞式IO,都是同步IO模型,區別就在與第一步是否完成後才返回,但第二步都需要當前程序去完成,非同步IO呢,就是從第一步開始就返回,直到第二步完成後才會返回一個訊息,也就是說,非阻塞能夠讓你在第一步時去做其它的事情,而真正的非同步IO能讓你第二步的過程也能去做其它事情。這裡就在說一下select,poll和epoll這幾個IO複用方式,這時你就會了解它們為什麼是同步IO了,以epoll為例,在epoll開發的伺服器模型中,epoll_wait()這個函式會阻塞等待就緒的fd,將就緒的fd拷貝到epoll_events集合這個過程中也不能做其它事(雖然這段時間很短,所以epoll配合非阻塞IO是很高效也是很普遍的伺服器開發模式--同步非阻塞IO模型)。有人把epoll這種方式叫做同步非阻塞(NIO),因為使用者執行緒需要不停地輪詢,自己讀取資料,看上去好像只有一個執行緒在做事情,

也有人把這種方式叫做非同步非阻塞(AIO),因為畢竟是核心執行緒負責掃描fd列表,並填充事件連結串列的,個人認為真正理想的非同步非阻塞,應該是核心執行緒填充事件連結串列後,主動通知使用者執行緒,或者呼叫應用程式事先註冊的回撥函式來處理資料,如果還需要使用者執行緒不停的輪詢來獲取事件資訊,就不是太完美了,所以也有不少人認為epoll是偽AIO,還是有道理的。

再生動一點,還是以接水為例,當你使用epoll的IO複用時,相當於接水方有了一個服務員,他會告訴你哪些水龍頭有水可以接,然後你可以阻塞或者非阻塞(最好是非阻塞,不然epoll的意義就不大了)的去接水,但接水這個過程,還是要你自己完成。而非同步的IO模型呢,相當於你去水房,直接把水杯遞給服務員,讓他給你接好後再通知你來,連線水的過程也不需要了,非同步IO的模型如下圖: