同步IO、異步IO、阻塞IO、非阻塞IO之間的聯系與區別
POSIX
同步IO、異步IO、阻塞IO、非阻塞IO,這幾個詞常見於各種各樣的與網絡相關的文章之中,往往不同上下文中它們的意思是不一樣的,以致於我在很長一段時間對此感到困惑,所以想寫一篇文章整理一下。
POSIX(可移植操作系統接口)把同步IO操作定義為導致進程阻塞直到IO完成的操作,反之則是異步IO
按POSIX的描述似乎把同步和阻塞劃等號,異步和非阻塞劃等號,但是為什麽有的人說同步IO不等於阻塞IO呢?先來說說幾種常見的IO模型吧。
IO模型
這裏統一使用Linux下的系統調用recv作為例子,它用於從套接字上接收一個消息,因為是一個系統調用,所以調用時會從用戶進程空間切換到內核空間運行一段時間再切換回來
- 阻塞IO模型
使用recv的默認參數一直等數據直到拷貝到用戶空間,這段時間內進程始終阻塞。A同學用杯子裝水,打開水龍頭裝滿水然後離開。這一過程就可以看成是使用了阻塞IO模型,因為如果水龍頭沒有水,他也要等到有水並裝滿杯子才能離開去做別的事情。很顯然,這種IO模型是同步的。
- 非阻塞IO模型
改變flags,讓recv不管有沒有獲取到數據都返回,如果沒有數據那麽一段時間後再調用recv看看,如此循環。B同學也用杯子裝水,打開水龍頭後發現沒有水,它離開了,過一會他又拿著杯子來看看……在中間離開的這些時間裏,B同學離開了裝水現場(回到用戶進程空間),可以做他自己的事情。這就是非阻塞IO模型。但是它只有是檢查無數據的時候是非阻塞的,在數據到達的時候依然要等待復制數據到用戶空間(等著水將水杯裝滿),因此它還是同步IO。
- IO復用模型
這裏在調用recv前先調用select或者poll,這2個系統調用都可以在內核準備好數據(網絡數據到達內核)時告知用戶進程,這個時候再調用recv一定是有數據的。因此這一過程中它是阻塞於select或poll,而沒有阻塞於recv,有人將非阻塞IO定義成在讀寫操作時沒有阻塞於系統調用的IO操作(不包括數據從內核復制到用戶空間時的阻塞,因為這相對於網絡IO來說確實很短暫),如果按這樣理解,這種IO模型也能稱之為非阻塞IO模型,但是按POSIX來看,它也是同步IO,那麽也和樓上一樣稱之為同步非阻塞IO吧。
這種IO模型比較特別,分個段。因為它能同時監聽多個文件描述符(fd)。這個時候C同學來裝水,發現有一排水龍頭,舍管阿姨告訴他這些水龍頭都還沒有水,等有水了告訴他。於是等啊等(select調用中),過了一會阿姨告訴他有水了,但不知道是哪個水龍頭有水,自己看吧。於是C同學一個個打開,往杯子裏裝水(recv)。這裏再順便說說鼎鼎大名的epoll(高性能的代名詞啊),epoll也屬於IO復用模型,主要區別在於舍管阿姨會告訴C同學哪幾個水龍頭有水了,不需要一個個打開看(當然還有其它區別)。
- 信號驅動IO模型
通過調用sigaction註冊信號函數,等內核數據準備好的時候系統中斷當前程序,執行信號函數(在這裏面調用recv)。D同學讓舍管阿姨等有水的時候通知他(註冊信號函數),沒多久D同學得知有水了,跑去裝水。是不是很像異步IO?很遺憾,它還是同步IO(省不了裝水的時間啊)。
- 異步IO模型
調用aio_read,讓內核等數據準備好,並且復制到用戶進程空間後執行事先指定好的函數。E同學讓舍管阿姨將杯子裝滿水後通知他。整個過程E同學都可以做別的事情(沒有recv),這才是真正的異步IO。
總結
IO分兩階段:
1.數據準備階段
2.內核空間復制回用戶進程緩沖區階段
一般來講:阻塞IO模型、非阻塞IO模型、IO復用模型(select/poll/epoll)、信號驅動IO模型都屬於同步IO,因為階段2是阻塞的(盡管時間很短)。只有異步IO模型是符合POSIX異步IO操作含義的,不管在階段1還是階段2都可以幹別的事。
- ps:以上圖片均截自UNIX網絡編程卷1。
同步IO、異步IO、阻塞IO、非阻塞IO之間的聯系與區別