天天看點

同步、異步、阻塞、非阻塞、BIO/NIO/AIO/IO複用模型一、什麼是socket?什麼是I/O操作?二、同步、異步、阻塞、非阻塞

一、什麼是socket?什麼是I/O操作?

        我們都知道unix(like)世界裡,一切皆檔案,而檔案是什麼呢?檔案就是一串二進制流而已,不管socket,還是FIFO、管道、終端,對我們來說,一切都是檔案,一切都是流。在資訊 交換的過程中,我們都是對這些流進行資料的收發操作,簡稱為I/O操作(input and output),往流中讀出資料,系統調用read,寫入資料,系統調用write。不過話說回來了 ,計算機裡有這麼多的流,我怎麼知道要操作哪個流呢?對,就是檔案描述符,即通常所說的fd,一個fd就是一個整數,是以,對這個整數的操作,就是對這個檔案(流)的操作。我們建立一個socket,通過系統調用會傳回一個檔案描述符,那麼剩下對socket的操作就會轉化為對這個描述符的操作。不能不說這又是一種分層和抽象的思想。

二、同步、異步、阻塞、非阻塞

    實際上同步與異步是針對應用程式與核心的互動而言的。同步過程中程序觸發IO操作并等待(也就是我們說的阻塞)或者輪詢的去檢視IO操作(也就是我們說的非阻塞)是否完成。 異步過程中程序觸發IO操作以後,直接傳回,做自己的事情,IO交給核心來處理,完成後核心通知程序IO完成。

同步和異步針對應用程式來,關注的是程式中間的協作關系;

阻塞與非阻塞更關注的是單個程序的執行狀态。

同步有阻塞和非阻塞之分,異步沒有,它一定是非阻塞的。

阻塞、非阻塞、多路IO複用,都是同步IO,異步必定是非阻塞的,是以不存在異步阻塞和異步非阻塞的說法。真正的異步IO需要CPU的深度參與。換句話說,隻有使用者線程在操作IO的時候根本不去考慮IO的執行全部都交給CPU去完成,而自己隻等待一個完成信号的時候,才是真正的異步IO。是以,拉一個子線程去輪詢、去死循環,或者使用select、poll、epool,都不是異步。

  • 同步:執行一個操作之後,程序觸發IO操作并等待(也就是我們說的阻塞)或者輪詢的去檢視IO操作(也就是我們說的非阻塞)是否完成,等待結果,然後才繼續執行後續的操作。
  • 異步:執行一個操作後,可以去執行其他的操作,然後等待通知再回來執行剛才沒執行完的操作。
  • 阻塞:程序給CPU傳達一個任務之後,一直等待CPU處理完成,然後才執行後面的操作。
  • 非阻塞:程序給CPU傳達任我後,繼續處理後續的操作,隔斷時間再來詢問之前的操作是否完成。這樣的過程其實也叫輪詢。

故事:老王燒開水。

出場人物:老張,水壺兩把(普通水壺,簡稱水壺;會響的水壺,簡稱響水壺)。

老王想了想,有好幾種等待方式

1.【同步阻塞】老王用水壺煮水,并且站在那裡,不管水開沒開,每隔一定時間看看水開了沒。

老王想了想,這種方法不夠聰明。

2.【同步非阻塞】老王還是用水壺煮水,不再傻傻的站在那裡看水開,跑去寝室上網,但是還是會每隔一段時間過來看看水開了沒有,水沒有開就走人。

老王想了想,現在的方法聰明了些,但是還是不夠好。

3.【異步阻塞】老王這次使用高大上的響水壺來煮水,站在那裡,但是不會再每隔一段時間去看水開,而是等水開了,水壺會自動的通知他。-

老王想了想,不會呀,既然水壺可以通知我,那我為什麼還要傻傻的站在那裡等呢,嗯,得換個方法。

4.【異步非阻塞】老王還是使用響水壺煮水,跑到客廳上網去,等着響水壺自己把水煮熟了以後通知他。-

老王豁然,這下感覺輕松了很多。

  • 同步和異步

    同步就是燒開水,需要自己去輪詢(每隔一段時間去看看水開了沒),異步就是水開了,然後水壺會通知你水已經開了,你可以回來處理這些開水了。

    同步和異步是相對于操作結果來說,會不會等待結果傳回。

  • 阻塞和非阻塞

    阻塞就是說在煮水的過程中,你不可以去幹其他的事情,非阻塞就是在同樣的情況下,可以同時去幹其他的事情。阻塞和非阻塞是相對于線程是否被阻塞。

三、詳細介紹

網絡IO的模型大緻包括下面幾種

  • 同步模型(synchronous IO)
    • 阻塞IO(bloking IO)
    • 非阻塞IO(non-blocking IO)
    • 多路複用IO(multiplexing IO)
    • 信号驅動式IO(signal-driven IO)
  • 異步IO(asynchronous IO)
    • 異步IO

網絡IO的本質是socket的讀取,socket在linux系統被抽象為流,IO可以了解為對流的操作。對于一次IO通路,資料會先被拷貝到作業系統核心的緩沖區中,然後才會從作業系統核心的緩沖區拷貝到應用程式的位址空間,是以一般會經曆兩個階段:

  1. 等待所有資料都準備好或者一直在等待資料,有資料的時候将資料拷貝到系統核心;
  2. 将核心緩存中資料拷貝到使用者程序中;

對于socket流而言:

  1. 等待網絡上的資料分組到達,然後被複制到核心的某個緩沖區;
  2. 把資料從核心緩沖區複制到應用程序緩沖區中;

3.1 阻塞IO

3.1.1 介紹

這也是最常用的模型,預設情況下所有的套接字都是 

阻塞

 的;

同步、異步、阻塞、非阻塞、BIO/NIO/AIO/IO複用模型一、什麼是socket?什麼是I/O操作?二、同步、異步、阻塞、非阻塞

我們把recvfrom函數視為系統調用,因為我們正區分程序和核心,系統調用一般都會從在應用程序空間中運作切換到核心空間中運作,一段時間後又再切換回來;

我們可以從圖中看到,應用程序從 

進行系統調用

 到 

複制資料報到應用程序的緩沖區完成

 的整段時間内是被阻塞的;在這個過程中,要麼正确到達,要麼系統調用被信号打斷;直到資料報被複制到使用者程序完成後,使用者程序才解除阻塞的狀态,當然,這是使用者程序自己進行的阻塞;

3.1.2 優點和缺點

  • 優點:能夠及時傳回資料,無延遲;友善調試;
  • 缺點:需要付出等待的代價;

3.2 非阻塞IO

3.2.1 介紹

非阻塞,當所請求的I/O操作非得把目前程序設定成睡眠才能完成時,不要把目前程序設定成睡眠,而是傳回一個錯誤資訊(資料報沒有準備好的情況下),此時目前程序可以做其它的事情,不用阻塞;

同步、異步、阻塞、非阻塞、BIO/NIO/AIO/IO複用模型一、什麼是socket?什麼是I/O操作?二、同步、異步、阻塞、非阻塞

從圖中可以得知,前三次系統調用時都沒有資料可以傳回,核心均傳回一個 

EWOULDBLOCK

,并且不會阻塞目前程序,直到第四次詢問核心緩沖區是否有資料的時候,此時核心緩沖區中已經有一個準備好的資料,是以将核心資料複制到使用者空間,此時系統調用則傳回成功;

當一個應用程序像這樣對一個非阻塞socket循環調用 

recv/recvfrom

 時,則稱為輪詢;應用程序持續輪詢核心,以檢視某個操作是否就緒,這麼做往往消耗大量的CPU時間。

3.2.2 優點和缺點

  • 優點:相較于阻塞模型,非阻塞不用再等待任務,而是把時間花費到其它任務上,也就是這個目前線程同時處理多個任務;
  • 缺點:導緻任務完成的響應延遲增大了,因為每隔一段時間才去執行詢問的動作,但是任務可能在兩個詢問動作的時間間隔内完成,這會導緻整體資料吞吐量的降低。

3.3 IO多路複用

3.3.1 介紹

有了I/O複用,我們就可以調用 

select或poll

,讓其阻塞在兩個系統調用(1.詢問資料是否準備好并且直到資料準備好才傳回;2.核心是否把資料全部複制完成到使用者程序)中的某一個之上

同步、異步、阻塞、非阻塞、BIO/NIO/AIO/IO複用模型一、什麼是socket?什麼是I/O操作?二、同步、異步、阻塞、非阻塞

圖中阻塞于 

select

 調用,等待資料報套接字變為可讀。當select傳回套接字可讀這一條件的時候,則調用 

recvfrom

 把所讀資料報複制到應用程序緩沖區;

之前的同步非阻塞方式需要使用者程序不停的輪詢,但是IO多路複用不需要不停的輪詢,而是派别人去幫忙循環查詢多個任務的完成狀态,UNIX/Linux 下的 

select、poll、epoll

 就是幹這個的;select調用是核心級别的,select輪詢相對非阻塞的輪詢的差別在于---前者可以等待多個socket,能實作同時對多個IO端口進行監聽,當其中任何一個socket的資料準好了,就能傳回進行可讀,然後程序再進行recvform系統調用,将資料由核心拷貝到使用者程序,當然這個過程是阻塞的。select或poll調用之後,會阻塞程序,與blocking IO阻塞不同在于,此時的select不是等到socket資料全部到達再處理, 而是有了一部分資料(網絡上的資料是分組到達的)就會調用使用者程序來處理。如何知道有一部分資料到達了呢?監視的事情交給了核心,核心負責資料到達的處理。

我認為上面那句話中存在兩個重要點:1.對多個socket進行監聽,隻要任何一個socket資料準備好就傳回可讀;2.不等一個socket資料全部到達再處理,而是一部分socket的資料到達了就通知使用者程序;

其實 

select、poll、epoll

 的原理就是不斷的周遊所負責的所有的socket完成狀态,當某個socket有資料到達了,就傳回可讀并通知使用者程序來處理;

3.3.2 優點和缺點

  • 優點:能夠同時處理多個連接配接,系統開銷小,系統不需要建立新的額外程序或者線程,也不需要維護這些程序和線程的運作,降低了系統的維護工作量,節省了系統資源。
  • 缺點:如果處理的連結數目不高的話,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延遲還更大。(因為阻塞可以保證沒有延遲,但是多路複用是處理先存在的資料,是以資料的順序則不管,導緻處理一個完整的任務的時間上有延遲)

3.3.3 同步非阻塞和多線程+同步阻塞

高并發的程式一般使用同步非阻塞方式而非多線程 + 同步阻塞方式。要了解這一點,首先要扯到并發和并行的差別。比如去某部門辦事需要依次去幾個視窗,辦事大廳裡的人數就是并發數,而視窗個數就是并行度。也就是說并發數是指同時進行的任務數(如同時服務的 HTTP 請求),而并行數是可以同時工作的實體資源數量(如 CPU 核數)。通過合理排程任務的不同階段,并發數可以遠遠大于并行度,這就是區區幾個 CPU 可以支援上萬個使用者并發請求的奧秘。在這種高并發的情況下,為每個任務(使用者請求)建立一個程序或線程的開銷非常大。而同步非阻塞方式可以把多個 IO 請求丢到背景去,這就可以在一個程序裡服務大量的并發 IO 請求。

3.4 信号驅動式I/O模型

同步、異步、阻塞、非阻塞、BIO/NIO/AIO/IO複用模型一、什麼是socket?什麼是I/O操作?二、同步、異步、阻塞、非阻塞

首先開啟套接字的信号驅動式IO功能,并且通過 

sigaction

 系統調用安裝一個信号處理函數,該函數調用将立即傳回,目前程序沒有被阻塞,繼續工作;當資料報準備好的時候,核心則為該程序産生 

SIGIO

 的信号,随後既可以在信号處理函數中調用 

recvfrom

 讀取資料報,并且通知主循環資料已經準備好等待處理,也可以通知主循環讓它讀取資料報;(其實就是一個待讀取的通知和待處理的通知);

3.5 異步式I/O模型

同步、異步、阻塞、非阻塞、BIO/NIO/AIO/IO複用模型一、什麼是socket?什麼是I/O操作?二、同步、異步、阻塞、非阻塞

我們調用 

aio_read

 函數,給核心傳遞描述符、緩沖區指針、緩沖區大小和檔案偏移,并且告訴核心當整個操作完成時如何通知我們。該函數調用後立即傳回,不被阻塞;

同步、異步、阻塞、非阻塞、BIO/NIO/AIO/IO複用模型一、什麼是socket?什麼是I/O操作?二、同步、異步、阻塞、非阻塞

3.6 比較

同步、異步、阻塞、非阻塞、BIO/NIO/AIO/IO複用模型一、什麼是socket?什麼是I/O操作?二、同步、異步、阻塞、非阻塞

繼續閱讀