上次寫了一篇文章: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。
原文鏈接:http://blogread.cn/it/article/4108?f=wb
原創文章,作者:追馬,如若轉載,請注明出處:http://www.www58058.com/2333