同步(synchronous) io和非同步(asynchronous) io,阻塞(blocking) io和非阻塞(non-blocking)io分別是什麼,到底有什麼區別?這個問題其實不同的人給出的答案都可能不同,比如wiki,就認為asynchronous io和non-blocking io是乙個東西。這其實是因為不同的人的知識背景不同,並且在討論這個問題的時候上下文(context)也不相同。所以,為了更好的回答這個問題,我先限定一下本文的上下文。本文討論的背景是linux環境下的network io。
本文最重要的參考文獻是richardstevens的「unix® network programmingvolume 1, third edition: the sockets networking
」,6.2節「i/o models
」,stevens在這節中詳細說明了各種io的特點和區別,如果英文夠好的話,推薦直接閱讀。stevens的文風是有名的深入淺出,所以不用擔心看不懂。本文中的流程圖也是擷取自參考文獻。
stevens在文章中一共比較了五種io model:
blocking io
nonblocking io
io multiplexing
signal driven io
asynchronous io
由於signal driven io在實際中並不常用,所以我這只提及剩下的四種io model。
再說一下io發生時涉及的物件和步驟。以read函式舉例,對於乙個networkio會涉及到兩個系統物件,乙個是呼叫這個io的process (or thread),另乙個就是系統核心(kernel)。當乙個read操作發生時,它會經歷兩個階段:
(1)等待資料準備(waitingfor the data to be ready)
(2)將資料從核心拷貝到程序中(copyingthe data from the kernel to the process)
記住這兩點很重要,因為這些
io model
的區別就是在兩個階段上各有不同的情況。是否阻塞說的是第乙個階段,即等待資料準備階段是否會阻塞,而是否同步說的是第二階段,即將資料從核心拷貝到程序這個真實的
io operation
操作階段是否阻塞。
在linux中,預設情況下所有的socket都是blocking,乙個典型的讀操作流程大概是這樣:
當使用者程序呼叫了recvfrom這個系統呼叫,kernel就開始了io的第乙個階段:準備資料。對於network io來說,很多時候資料在一開始還沒有到達(比如,還沒有收到乙個完整的udp包),這個時候kernel就要等待足夠的資料到來。而在使用者程序這邊,整個程序會被阻塞。當kernel一直等到資料準備好了,它就會將資料從kernel中拷貝到使用者記憶體,然後kernel返回結果,使用者程序才解除block的狀態,重新執行起來。
所以,blocking io
的特點就是在
io執行的兩個階段都被
block
了。linux下,可以通過設定socket使其變為non-blocking。當對乙個non-blockingsocket執行讀操作時,流程是這個樣子:
從圖中可以看出,當使用者程序發出read操作時,如果kernel中的資料還沒有準備好,那麼它並不會block使用者程序,而是立刻返回乙個error。從使用者程序角度講,它發起乙個read操作後,並不需要等待,而是馬上就得到了乙個結果。使用者程序判斷結果是乙個error時,它就知道資料還沒有準備好,於是它可以再次傳送read操作。一旦kernel中的資料準備好了,並且又再次收到了使用者程序的system call,那麼它馬上就將資料拷貝到了使用者記憶體,然後返回。所以,使用者程序其實是需要不斷的主動詢問kernel資料好了沒有。
所以,non-blocking io
的特點就是在
io執行的第一階段不阻塞,若沒準備好即可返回,但是在第二階段即將資料從核心拷貝到程序這個真實的
io operation
操作階段會阻塞。
io multiplexing這個詞可能有點陌生,但是如果我說select/epoll,大概就都能明白了。有些地方也稱這種io方式為事件驅動io(event-driven io)。我們都知道,select/epoll的好處就在於單個process就可以同時處理多個網路連線的io。它的基本原理就是select/epoll這個function會不斷的輪詢所負責的所有socket,當某個socket有資料到達了,就通知使用者程序。它的流程如圖:
當使用者程序呼叫了select,那麼整個程序會被block,而同時,kernel會「監視」所有select負責的socket,當任何乙個socket中的資料準備好了,select就會返回。這個時候使用者程序再呼叫read操作,將資料從kernel拷貝到使用者程序。
這個圖和blocking io的圖其實並沒有太大的不同,事實上,還更差一些。因為這裡需要使用兩個system call (select 和 recvfrom),而blocking io只呼叫了乙個system call (recvfrom)。但是,用select的優勢在於它可以同時處理多個connection。(多說一句。所以,如果處理的連線數不是很高的話,使用select/epoll的web server不一定比使用multi-threading + blocking io的web server效能更好,可能延遲還更大。select/epoll的優勢並不是對於單個連線能處理得更快,而是在於能處理更多的連線。)
在io multiplexingmodel中,實際中,對於每乙個socket,一般都設定成為non-blocking,但是,如上圖所示,整個使用者的process其實是一直被block的。只不過process是被select這個函式block,而不是被socket io給block。
所以多路復用
io的特點是引入了
select
這樣乙個過程,阻塞監視多個
socket
,一旦有某個
socket
資料準備好了,則返回執行同步io。
linux下的asynchronousio其實用得很少。先看一下它的流程:
使用者程序發起read操作之後,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到乙個asynchronous read之後,首先它會立刻返回,所以不會對使用者程序產生任何block。然後,kernel會等待資料準備完成,然後將資料拷貝到使用者記憶體,當這一切都完成之後,kernel會給使用者程序傳送乙個signal,告訴它read操作完成了。
所以非同步
io的特點是,在
io操作的兩個階段都不會阻塞,而是全權交給作業系統核心來完成,而核心完成後通過訊號來通知使用者程序即可。
區別在於
io操作的第一階段,呼叫
blockingio
會一直block
住對應的程序直到操作完成,而
non-blocking io
在kernel
還準備資料的情況下會立刻返回。
在說明synchronous io和asynchronousio的區別之前,需要先給出兩者的定義。stevens給出的定義(其實是posix的定義)是這樣子的:
a synchronous i/ooperation causes the requesting process to be blocked until that
i/o operation
completes;
an asynchronous i/o operationdoes not cause the requesting process to be blocked;
兩者的區別就在於
synchronous io
做」io operation」
的時候會將
process
阻塞。按照這個定義,之前所述的
blockingio
,non-blocking io
,io multiplexing
都屬於synchronous io
。有人可能會說,non-blockingio並沒有被block啊。這裡有個非常「狡猾」的地方,定義中所指的」io operation」是指真實的io操作,就是例子中的recvfrom這個system call。non-blocking io在執行recvfrom這個system call的時候,如果kernel的資料沒有準備好,這時候不會block程序。但是,當kernel中資料準備好的時候,recvfrom會將資料從kernel拷貝到使用者記憶體中,這個時候程序是被block了,在這段時間內,程序是被block的。而asynchronous io則不一樣,當程序發起io 操作之後,就直接返回再也不理睬了,直到kernel傳送乙個訊號,告訴程序說io完成。在這整個過程中,程序完全沒有被block。
各個io model的比較如圖所示:
經過上面的介紹,會發現non-blockingio和asynchronous io的區別還是很明顯的。在non-blocking io中,雖然程序大部分時間都不會被block,但是它仍然要求程序去主動的check,並且當資料準備完成以後,也需要程序主動的再次呼叫recvfrom來將資料拷貝到使用者記憶體。而asynchronous io則完全不同。它就像是使用者程序將整個io操作交給了他人(kernel)完成,然後他人做完後發訊號通知。在此期間,使用者程序不需要去檢查io操作的狀態,也不需要主動的去拷貝資料。
阻塞式 非阻塞式IO
知識點 非阻塞式io 的兩種設定方法 1 函式fcntl 設定 o nonblock 選項 int flag fcntl sockfd,f getfl,0 檢查檔案標誌位 fcntl sockfd,f setfl,flag o nonblock 設定檔案標誌位 2 函式ioctl 設定fionbio...
阻塞I O,非阻塞I O
拿 socket舉例。當read資料時,如果這時沒有資料可讀,阻塞i o會一直等待有資料讀,資料從kernel copy 到socket的buffer後返回 非阻塞i o會立即返回,但如果有資料可讀,非阻塞i o也是等資料從kernel copy 到socket的buffer後返回。以上是阻塞與非阻...
非阻塞IO和阻塞IO
非阻塞io和阻塞io 在網路程式設計中對於乙個網路控制代碼會遇到阻塞io 和非阻塞io 的概念,這裡對於這兩種socket 先做一下說明 基本概念 阻塞io socket 的阻塞模式意味著必須要做完io 操作 包括錯誤 才會返回。非阻塞io 非阻塞模式下無論操作是否完成都會立刻返回,需要通過其他方式...