IO中同步 非同步與阻塞 非阻塞的區別

2021-07-03 05:59:30 字數 4016 閱讀 2732

一、同步與非同步

概念解釋

同步和非同步 是針對應用程式和核心的互動而言的

同步指的是使用者程序觸發 io 操作並等待或者輪詢的去檢視 io 操作是否完成

而非同步是指使用者程序觸發 io 操作以後便開始做自己的事情,而當 io 操作已經完成的時候會得到 io 完成的通知。

二、阻塞與非阻塞

阻塞/非阻塞, 它們是程式在等待訊息(無所謂同步或者非同步)時的狀態.

概念解釋

a. 阻塞

阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起。函式只有在得到結果之後才會返回。

有人也許會把阻塞呼叫和同步呼叫等同起來,實際上他是不同的。

對於同步呼叫來說,很多時候當前執行緒還是啟用的,只是從邏輯上當前函式沒有返回而已。

socket接收資料函式recv是乙個阻塞呼叫的例子。

當socket工作在阻塞模式的時候, 如果沒有資料的情況下呼叫該函式,則當前執行緒就會被掛起,直到有資料為止。

b. 非阻塞

非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函式不會阻塞當前執行緒,而會立刻返回。

c. 物件的阻塞模式和阻塞函式呼叫

物件是否處於阻塞模式和函式是不是阻塞呼叫有很強的相關性,但是並不是一一對應的。

阻塞物件上可以有非阻塞的呼叫方式,我們可以通過一定的api去輪詢狀態,

在適當的時候呼叫阻塞函式,就可以避免阻塞。

而對於非阻塞物件,呼叫特殊的函式也可以進入阻塞呼叫。函式select就是這樣的乙個例子。

三、易混淆的點

很多人也會把非同步和非阻塞混淆,

因為非同步操作一般都不會在真正的io 操作處被阻塞,

比如如果用select 函式,當select 返回可讀時再去read 一般都不會被阻塞

就好比當你的號碼排到時一般都是在你之前已經沒有人了,所以你再去櫃檯辦理業務就不會被阻塞.

可見,同步/非同步與阻塞/非阻塞是兩組不同的概念,它們可以共存組合,

而很多人之所以把同步和阻塞混淆,我想也是因為沒有區分這兩個概念,

比如阻塞的read/write 操作中,其實是把訊息通知和處理訊息結合在了一起,

在這裡所關注的訊息就是fd 是否可讀/寫,而處理訊息則是對fd 讀/寫.

當我們將這個fd 設定為非阻塞的時候,read/write 操作就不會在等待訊息通知這裡阻塞,

如果fd 不可讀/寫則操作立即返回.

再說一下io發生時涉及的物件和步驟。

對於乙個network io (這裡我們以read舉例),它會涉及到兩個系統物件,乙個是呼叫這個io的process (or thread),另乙個就是系統核心(kernel)。當乙個read操作發生時,它會經歷兩個階段:

1 等待資料準備 (waiting for the data to be ready)

2 將資料從核心拷貝到程序中 (copying the data from the kernel to the process)

記住這兩點很重要,因為這些io model的區別就是在兩個階段上各有不同的情況。

阻塞io

在linux中,預設情況下所有的socket都是blocking,乙個典型的讀操作流程大概是這樣:

當使用者程序呼叫了recvfrom這個系統呼叫,kernel就開始了io的第乙個階段:準備資料。對於network io來說,很多時候資料在一開始還沒有到達(比如,還沒有收到乙個完整的udp包),這個時候kernel就要等待足夠的資料到來。當kernel一直等到資料準備好了,它就會將資料從kernel中拷貝到使用者記憶體,然後返回結果。

非阻塞io

linux下,可以通過設定socket使其變為非阻塞。當對乙個非阻塞 socket執行讀操作時,流程是這個樣子:

從圖中可以看出,當使用者程序發出read操作時,如果kernel中的資料還沒有準備好,那麼它並不會block使用者程序,而是立刻返回乙個error。從使用者程序角度講 ,它發起乙個read操作後,並不需要等待,而是馬上就得到了乙個結果。使用者程序判斷結果是乙個error時,它就知道資料還沒有準備好,於是它可以再次傳送read操作。一旦kernel中的資料準備好了,並且又再次收到了使用者程序的system call,那麼它馬上就將資料拷貝到了使用者記憶體,然後返回。

所以,使用者程序其實是需要不斷的主動詢問kernel資料好了沒有。

io復用

io 復用這個詞可能有點陌生,但是如果我說select,epoll,大概就都能明白了。有些地方也稱這種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 multiplexing model中,實際中,對於每乙個socket,一般都設定成為non-blocking,但是,如上圖所示,整個使用者的process其實是一直被block的。只不過process是被select這個函式block,而不是被socket io給block。

非同步 i/o

linux下的非同步 io其實用得很少。先看一下它的流程:

使用者程序發起read操作之後,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到乙個asynchronous read之後,首先它會立刻返回,所以不會對使用者程序產生任何block。然後,kernel會等待資料準備完成,然後將資料拷貝到使用者記憶體,當這一切都完成之後,kernel會給使用者程序傳送乙個signal,告訴它read操作完成了。

到目前為止,已經將四個io model都介紹完了。阻塞和非阻塞的區別在哪,同步io和非同步io的區別在哪。

先回答最簡單的這個:阻塞vs非阻塞。前面的介紹中其實已經很明確的說明了這兩者的區別。呼叫阻塞io會一直阻塞住對應的程序直到操作完成,而非阻塞 io在kernel還準備資料的情況下會立刻返回。

各個io model的比較如圖所示:

經過上面的介紹,會發現非阻塞 io和非同步 io的區別還是很明顯的。在非阻塞 io中,雖然程序大部分時間都不會被阻塞,但是它仍然要求程序去主動的check,並且當資料準備完成以後,也需要程序主動的再次呼叫recvfrom來將資料拷貝到使用者記憶體。而非同步 io則完全不同。它就像是使用者程序將整個io操作交給了他人(kernel)完成,然後他人做完後發訊號通知。在此期間,使用者程序不需要去檢查io操作的狀態,也不需要主動的去拷貝資料。

IO 同步,非同步,阻塞,非阻塞

參考文章 好文推薦 唉最近真是高產似母豬,剛進新公司工作量暫時不飽和,只能每天學學學學學學查漏補缺啦,學習使我快樂哈哈哈哈哈哈哈哈 標題裡的詞彙相信都經常看到,但是能說清楚的估計20個人裡面能有1個就不錯了,網上的資料也是五花八門,大部分描述差不多,很多時候估計作者本身也是似懂非懂,我也看了很多文章...

同步 非同步 阻塞 非阻塞 I O

一 同步 非同步 首先要是多個事物,只有乙個事物,是不存在同步或非同步的。同步 指協同步調。即,多個事物不能同時進行,必須乙個乙個的來,上乙個事物結束後,下乙個事物才開始。那當乙個事物正在進行時,其他事物在幹嘛呢?嚴格來講並沒有要求,但一般都處於 等待 狀態,因為後面事物的正常進行都需要依賴前面事物...

同步非同步IO,阻塞非阻塞

同步io操作 導致請求程序阻塞,知道io操作完成。非同步io操作 不導致程序阻塞。在處理 網路 io 的時候,阻塞和非阻塞都是同步io,阻塞,就是呼叫我 函式 我 函式 沒有接收完資料或者沒有得到結果之前,我不會返回。非阻塞,就是呼叫我 函式 我 函式 立即返回,通過select通知呼叫者 阻塞與非...