1. 程式人生 > >關於網路IO中的同步、非同步、阻塞、非阻塞

關於網路IO中的同步、非同步、阻塞、非阻塞

在高併發程式設計當中,我們經常會遇到一些非同步、非阻塞等一些概念,一些常用的技術比如非同步的httpclient、netty nio、nginx、node.js等,它們的原理大都跟非同步、非阻塞有關。特別是在伺服器開發中,併發的請求處理是個大問題,阻塞式的函式會導致資源浪費和時間延遲。通過事件註冊、非同步函式,開發人員可以提高資源的利用率,效能也會改善。其nginx和node.js處理併發都是採用的事件驅動非同步非阻塞模式。其中nginx中處理併發用的是epoll,poll,queue等方式,node.js使用的是libev,它們對大規模的HTTP請求處理的都很好。

那麼到底什麼是非同步、非阻塞,它們的原理是什麼,它們之間又有什麼區別呢?其實在很多情況下,非同步與非阻塞(同步與阻塞)表示的是同一個意思,但是在特定的上下文環境中,它們含義又十分不同。再具體講它們的區別之前,先介紹一下上下文背景。

一、上下文背景

我們所遇到的這些場景大部分都是當用戶程序(或執行緒)在進行網路IO時即進行Socket讀寫時遇到的,所以本文討論的上下文背景是基於Linux環境下的network IO。先介紹一下其中我們最常見的五種IO:

1.  blocking IO
2.  nonblocking IO
3.  IO multiplexing
4.  signal driven IO
5.  asynchronous IO

由於signal driven IO在實際中並不常用,所以我這隻提及剩下的四種IO Model。

再說一下IO發生時涉及的物件和步驟。對於一個network IO (這裡我們以read舉例),它會涉及到兩個系統物件,一個是呼叫這個IO的程序(或執行緒),另一個就是系統核心(kernel)。當一個read操作發生時,它會經歷兩個階段:

  1. 等待資料準備(Waiting for the data to be ready)
  2. 將資料從核心拷貝到程序中 (Copying the data from the kernel to the process)

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

二、各種IO介紹

2.1 blocking IO

在linux中,預設情況下所有的socket都是blocking,也就是說我們的一個程序在進行IO操作時如果沒有資料達到,這個程序是被阻塞的。一個典型的讀操作流程大概是這樣:
這裡寫圖片描述

當用戶程序呼叫了recvfrom這個系統呼叫,kernel就開始了IO的第一個階段:準備資料。對於network io來說,很多時候資料在一開始還沒有到達(比如,還沒有收到一個完整的UDP包),這個時候kernel就要等待足夠的資料到來。而在使用者程序這邊,整個程序會被阻塞。當kernel一直等到資料準備好了,它就會將資料從kernel中拷貝到使用者記憶體,然後kernel返回結果,使用者程序才解除block的狀態,重新執行起來。所以,blocking IO的特點就是在IO執行的wait和copy兩個階段都被block了

在這種block IO的情況下,如果請求的連線比較多,但其中大部分都是阻塞的。因為cpu的核數是有限的,所以一般的解決方案就是每個cpu啟用多個執行緒來處理多個連線。這種解決方案有很大的缺陷:

1. 執行緒是有記憶體開銷的,1個執行緒可能需要512K(或2M)存放棧,那麼1000個執行緒就要512M(或2G)記憶體
2. 執行緒的切換開銷和很大,因為執行緒切換時需要保持當前執行緒上下文資訊,當大量時間花在上下文切換的時候,分配給真正的操作的CPU就要少很多
3. 一個cpu所支援的執行緒數量時有限的(因為上面兩個原因),一般來說執行緒的數量級在幾百個左右就已經很大了

為了解決block IO存在的問題,就引入了no-blocking IO概念。

2.2 non-blocking IO

no-blocking IO很簡單,通過將socket設為非阻塞模式,這時,當你呼叫read時,如果有資料就緒,就返回資料,如果沒有資料就緒,就立刻返回一個錯誤,如EWOULDBLOCK。這樣是不會阻塞執行緒了,但是你還是要不斷的輪詢來讀取或寫入。當對一個non-blocking socket執行讀操作時,流程是這個樣子:
這裡寫圖片描述
從圖中可以看出,當用戶程序發出read操作時,如果kernel中的資料還沒有準備好,那麼它並不會block使用者程序,而是立刻返回一個error。從使用者程序角度講 ,它發起一個read操作後,並不需要等待,而是馬上就得到了一個結果。使用者程序判斷結果是一個error時,它就知道資料還沒有準備好,於是它可以再次傳送read操作。一旦kernel中的資料準備好了,並且又再次收到了使用者程序的system call,那麼它馬上就將資料拷貝到了使用者記憶體,然後返回。

從上面介紹可以看到,blocking IO的特點就是在IO執行的wait階段是非阻塞的,但是copy階段還是阻塞的

但是no-blocking IO也存在很大的缺陷,就是IO執行緒還是要不斷的輪詢socket來讀取或寫入, 於是,我們又引入了IO多路複用。

2.3 IO multiplexing(IO多路複用)

IO multiplexing即IO多路複用,有些地方也稱這種IO方式為event driven IO(事件驅動IO)。它的基本原理就是用通過作業系統提供的select/epoll等這些函式不斷的輪詢所負責的所有socket,而不是讓使用者程序自己去輪詢,注意這個socket必須先設成非同步的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,如果處理的連線數不是很高的話,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server效能更好,可能延遲還更大。select/epoll的優勢並不是對於單個連線能處理得更快,而是在於能處理更多的連線。

那麼IO多路複用的優勢在哪裡呢,其實就是在”多路複用”這個詞上。上面也講到了多路複用是指使用一個執行緒來檢查多個Socket(也成檔案描述符 )的就緒狀態,比如呼叫select和epoll函式,傳入多個檔案描述符,如果有一個檔案描述符就緒,則返回,否則阻塞直到超時。所以,在高併發的場景中,比如要處理10000個連線,只需要1個執行緒監控就緒狀態,對就緒的每個連線開一個執行緒處理或者直接丟到執行緒池處理,當然也可以用當前執行緒處理,那麼這個IO執行緒可以同時管理多個連線,也就是多路複用了。

2.4 Asynchronous I/O

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

三、各種IO之間的區別

到目前為止,已經將四個IO Model都介紹完了。現在回過頭來回答最初的那幾個問題:blocking和non-blocking的區別在哪,synchronous IO和asynchronous IO的區別在哪。

blocking vs non-blocking,這個問題很簡單,前面的介紹中其實已經很明確的說明了這兩者的區別:

1. blocking IO 會在wait和copy階段都會阻塞程序
2. non-blocking IO 在wait階段會立即返回不會阻塞程序,而在copy階段仍會阻塞程序copy資料

在說明synchronous IO和asynchronous IO的區別之前,需要先給出兩者的定義。Stevens給出的定義(其實是POSIX的定義)是這樣子的:

1. synchronous I/O:IO操作過程中程序會被阻塞,直到IO操作完成
2. asynchronous I/O:IO操作過程中程序不會被阻塞,作業系統幫你完成IO操作之後直接返回給你

按照這個定義,在網路IO層面,同步非同步相對於阻塞非阻塞是一個更加巨集觀的概念,之前所述的阻塞IO,非阻塞IO,IO多路複用都屬於同步IO,因為它們在核心copy資料階段都會阻塞程序。而非同步IO則不一樣,當程序發起IO 操作之後,就直接返回再也不理睬了,直到作業系統核心傳送一個訊號,告訴程序說作業系統IO已經完成,在這整個過程中,程序完全沒有被阻塞。

各個IO Model的比較如圖所示:
這裡寫圖片描述

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

相關推薦

關於網路IO同步非同步阻塞阻塞

在高併發程式設計當中,我們經常會遇到一些非同步、非阻塞等一些概念,一些常用的技術比如非同步的httpclient、netty nio、nginx、node.js等,它們的原理大都跟非同步、非阻塞有關。特別是在伺服器開發中,併發的請求處理是個大問題,阻塞式的函式會

網路程式設計阻塞阻塞同步非同步I/O模型的理解

1. 概念理解      在進行網路程式設計時,我們常常見到同步(Sync)/非同步(Async),阻塞(Block)/非阻塞(Unblock)四種呼叫方式:同步:所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是必須一件一件事做,等前一件做完了才能做下一件事。 例如

簡述linux同步非同步阻塞阻塞概念以及五種IO模型

1、概念剖析 相信很多從事linux後臺開發工作的都接觸過同步&非同步、阻塞&非阻塞這樣的概念,也相信都曾經產生過誤解,比如認為同步就是阻塞、非同步就是非阻塞,下面我們先剖析下這幾個概念分別是什麼含義。 同步:所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是必

python同步非同步阻塞阻塞

同步與非同步 同步和非同步關注的是訊息通訊機制. 同步非同步指的是呼叫者與被呼叫者兩者之間的關係,而不是經常容易誤解的多個被呼叫者之間的關係。 同步 所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不會返回,一旦呼叫返回,就得到返回值了。 換句話說,

阻塞阻塞同步非同步 五種IO模型

阻塞和非阻塞,同步和非同步1 例子故事:老王燒開水。出場人物:老張,水壺兩把(普通水壺,簡稱水壺;會響的水壺,簡稱響水壺)。老王想了想,有好幾種等待方式1.老王用水壺煮水,並且站在那裡,不管水開沒開,每隔一定時間看看水開了沒。-同步阻塞老王想了想,這種方法不夠聰明。2.老王還

socket阻塞阻塞同步非同步I/O模型(轉載只為查閱方便,若有侵權,立刪)

socket阻塞與非阻塞,同步與非同步 作者:huangguisu     1. 概念理解        在進行網路程式設計時,我們常常見到同步(Sync)/非同步(Async),阻塞(Block)/非阻塞(Unbl

阻塞阻塞同步非同步

阻塞和非阻塞 阻塞呼叫是指呼叫結果返回之前,呼叫者會進入阻塞狀態等待。只有在得到結果之後才會返回。 非阻塞呼叫是指在不能立刻得到結果之前,該函式不會阻塞當前執行緒,而會立刻返回。   2.同步與非同步 同步和非同步關注的是訊息通訊機制 (synchronous c

IO阻塞阻塞同步非同步及三種IO模型

什麼是同步和非同步?        燒水,我們都是通過熱水壺來燒水的。在很久之前,科技還沒有這麼發達的時候,如果我們要燒水,需要把水壺放到火爐上,我們通過觀察水壺內的水的沸騰程度來判斷水有沒有燒開。隨著科技的發展,現在市面上的水壺都有了提醒功能,當我們把水壺插電

python-同步非同步阻塞阻塞序列和並行並行和併發密集型執行緒和程序的相關概念

1. 同步和非同步   關注的是訊息的通訊機制,描述的是一種行為方式,是多個任務之間的關係。 ① 同步: 呼叫者主動等待被呼叫方返回結果,在沒有返回結果之前,就一直專職等待。 千萬不要把計算機中“同步”理解成“同時執行”。 ② 非同步:呼叫者傳送請求請求,不會專職等待

聊聊阻塞阻塞同步非同步I/O模型

1. 概念理解 在進行網路程式設計時,我們常常見到同步(Sync)/非同步(Async),阻塞(Block)/非阻塞(Unblock)四種呼叫方式: 同步/非同步主要針對C端:  同步: 所謂同步,就是在c端發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是必須一件一件事做,等前一件做完了才能做

同步非同步阻塞阻塞的理解

本質都是為了提高效率為目的 我做一件事情,請求外部協助。外部還沒響應結果,我要怎麼辦,一種辦法是,我一直等著對方給我答覆結果。另外一種是,對方主動通知我。這是同步和非同步的區別。 比如水壺,有沒有主動報警(當水開了時)的機制。 而我那個時候在幹嘛,我可以去幹別的工作,這種就是阻塞

聊聊同步非同步阻塞阻塞

近來遇到了一些常見的概念,尤其是網路程式設計方面的概念,如:阻塞、非阻塞、非同步I/O等等,對於這些概念自己也沒有太清晰的認識,只是很模糊的概念,說了解吧也瞭解,但是要讓自己準確的描述概念方面的具體細節,卻說的不那麼準確,這也是自己在這幾個方面也沒有細細考究過的原因吧。經過看

socket阻塞阻塞同步非同步I/O模型

1. 概念理解      在進行網路程式設計時,我們常常見到同步(Sync)/非同步(Async),阻塞(Block)/非阻塞(Unblock)四種呼叫方式: 同步/非同步主要針對C端: 同步:所謂同步,就是在c端發出一個功能呼叫時,在沒有得到結果之前,該

網路程式設計io總結區分。阻塞阻塞非同步同步

借鑑提煉自http://blog.csdn.net/historyasamirror/article/details/5778378 在網路程式設計中,肯定會有讀寫io的問題。這時候就會有了五種io模型。取自unix network  programing 五種模型分別是,

快速瞭解同步非同步阻塞阻塞

這篇文章純屬自己的理解,如有錯誤請您指出 同步和非同步: 同步和非同步是針對應用程式和核心互動而言 一、應用程式和核心同步互動有兩種情況:     1.應用程式等待一個操作執行完成     2.應用程式可以去做其他事,大需要時不時的過來檢視操作是否完成 二、應用程

同步非同步阻塞阻塞的理解與使用場景

首先來一張圖片大致瞭解一下這四個概念: 然後再來聽一聽鄙人的一些個人見解: 先解釋最基礎的概念, 之後用生活化的例子完成認知。 首先是blocking 和 non-blocking這兩個概念。 這兩個概念實質上是和IO沒有關係的,他們是在說,比如

socket阻塞阻塞同步非同步

1. 概念理解      在進行網路程式設計時,我們常常見到同步(Sync)/非同步(Async),阻塞(Block)/非阻塞(Unblock)四種呼叫方式:同步:所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是必須一件一件事做,等前一件做完了才能做下一件事。

easyui最新版本的TreeGrid同步樹形表格同步非同步樹形表格樹形表格分頁且非同步檢視子節點

     最近做一個專案,專案中開始使用的TreeTable的一個純js外掛。也許是對這個封裝的js不熟悉,不管怎麼除錯,出來的效果總是不太理想。沒得辦法,最後想起來easyUI對樹形表格的展示效果還不錯。     於是就根據easyUI官方最新的Demo做了下面的案例:

同步非同步阻塞阻塞

阻塞:主要針對執行緒來說,跟蹤原始碼最終會發現執行緒wait在某個方法上,等待被喚醒。在訊息通訊中,表現為來了資料後解除阻塞,比如阻塞佇列模型中的notFull.await()/notEmpty.signal()和notEmpty.await()/notFull.signa

Java多執行緒同步非同步阻塞阻塞

1、程序和執行緒的概念 程序:執行中的應用程式稱為程序,擁有系統資源(cpu、記憶體) 執行緒:程序中的一段程式碼,一個程序中可以有多段程式碼。本身不擁有資源(共享所在程序的資源); 在java中,程式入口被自動建立為主執行緒,在主執行緒中可以建立多個子執