天天看點

關于IO的同步,異步,阻塞,非阻塞

上次寫了一篇文章:Unix IO 模型學習。恰巧在這次周會的時候,@fp1203 (goldendoc成員之一) 正好在講解poll和epoll的底層實作。中途正好讨論了網絡IO的同步、異步、阻塞、非阻塞的概念,當時講下來,大家的了解各不相同,各執己見。搜尋了網絡上的一些文章,觀點也各不相同,甚至連wiki也将異步和非阻塞當成一個概念在解釋。

雖然網絡上充斥了大量關于同步、異步、阻塞、非阻塞的文章,但大都是抄來抄去,沒有一個權威的說法。但我找到了這一篇文章,該文章引用了《UNIX網絡程式設計 卷1》的介紹,這本書的作者是Richard Stevens。如果有Richard Stevens在這方面的定義或者結論,那麼我想,這應該是比較有說服力的了。

關于《UNIX網絡程式設計 卷1》這本書,我特意找了英文原版,也共享出來了:大家可以下載下傳《UNIX網絡程式設計 卷1》的英文原版?HM格式)。

我看了6.2這節内容,這節内容就是講IO模型的。剛剛提到的那篇文章,幾乎就是翻譯這個6.2節的。應該說,這個6.2節,對同步和異步的講解,算是很清楚的。

下面是我自己了解的重點。

IO模型

目前unix存在五種IO模型(這也和上一篇文章:Unix IO 模型 中提到的一緻),分别是:

· 阻塞型 IO(blocking I/O)

· 非阻塞性IO(nonblocking I/O)

· IO多路複用(I/O multiplexing)

· 信号驅動IO(signal driven I/O)

· 異步IO(asynchronous I/O)

IO的兩個階段

· 等待資料準備好

· 将資料從核心緩沖區複制到使用者程序緩沖區

同步,異步的差別

那麼究竟什麼是同步和異步的差別呢?請重點讀一下原文6.2節中的信号驅動IO和異步IO中的比較。最後總結出來是:

· 同步IO,需要使用者程序主動将存放在核心緩沖區中的資料拷貝到使用者程序中。

· 異步IO,核心會自動将資料從核心緩沖區拷貝到使用者緩沖區,然後再通知使用者。

這樣,同步和異步的概念就非常明顯了。以上的五種IO模型,前面四種都是同步的,隻有第五種IO模型才是異步的IO。

阻塞和非阻塞

那麼阻塞和非阻塞呢?注意到以上五個模型。阻塞IO,非阻塞IO,隻是上面的五個模型中的兩個。阻塞,非阻塞,是針對單個程序而言的。

當對多路複用IO進行調用時,比如使用poll。需注意的是,poll是系統調用,當調用poll的時候,其實已經是陷入了核心,是核心線程在跑了。是以對于調用poll的使用者程序來講,此時是阻塞的。

因為poll的底層實作,是去掃描每個檔案描述符(fd),而如果要對感興趣的fd進行掃描,那麼隻能将每個描述符設定成非阻塞的形式(對于使用者程序來講,設定fd是阻塞還是非阻塞,可以使用系統調用fcntl),這樣才有可能進行掃描。如果掃描當中,發現有可讀(如果可讀是使用者感興趣的)的fd,那麼select就在使用者程序層面就會傳回,并且告知使用者程序哪些fd是可讀的。

這時候,使用者程序仍然需要使用read的系統調用,将fd的資料,從核心緩沖區拷貝到使用者程序緩沖區(這也是poll為同步IO的原因)。

那麼此時的read是阻塞還是非阻塞呢?這就要看fd的狀态了,如果fd被設定成了非阻塞,那麼此時的read就是非阻塞的;如果fd被設定成了阻塞,那麼此時的read就是阻塞的。

不過程式已經執行到了這時候,不管fd是阻塞還是非阻塞,都沒有任何差別,因為之前的poll,就是知道有資料準備好了才傳回的,也就是說核心緩沖區已經有了資料,此時進行read,是肯定能夠将資料拷貝到使用者程序緩沖區的。

但如果換種想法,如果poll是因為逾時傳回的,而我們又對一個fd(此fd是被poll輪詢過的)進行read調用,那麼此時是阻塞還是非阻塞,就非常有意義了,對吧!

結論

· 判斷IO是同步還是異步,是看誰主動将資料拷貝到使用者程序。

· select或者poll,epoll,是同步調用,進行此調用的使用者程序也處于阻塞狀态。

· javaScript或者nodejs中的讀取網絡(檔案)資料,然後提供回調函數進行處理,是異步IO。

繼續閱讀