計算機I/O與I/O模型

你是哪塊小餅乾發表於2019-05-10
涉及應用程式效能的相關話題,一定少不了I/O,但是很多人的理解一直停留在I/O就是輸入輸出,電腦在磁碟上讀寫就是I/O,它非常耗費效能。這是比較膚淺的。

一、計算機基本組成

組成

在講述IO之前,首先要對計算機的組成有個大致的瞭解,組成計算機的三大件:CPU、記憶體、IO。

匯流排:就是一條或者多條物理上的導線,每個部件都接到這些導線上,同一時刻只能有一個部件在接收或者傳送。

仲裁匯流排:所有部件按照另一條匯流排,也就是仲裁匯流排或者中斷匯流排上給出的訊號來判斷這個時刻匯流排可以由哪個部件來使用。產生仲裁匯流排或者中斷電位的可以是CPU,也可以是匯流排上的其他裝置。

下圖的主機板上每個部件都是通過匯流排連線起來的。下圖中的各個元件的名稱及作用:

PCI匯流排:目前桌上型電腦與伺服器所普遍使用的一種南橋與外設連線的匯流排技術。

北橋晶片:IO匯流排和乙太網HUB模型的區別在於多了一個北橋晶片。因為CPU和記憶體足夠快,他們之間單獨用一個匯流排連線,這個匯流排和慢速IO匯流排之間通過一個橋接晶片連線,也就是主機板上的北橋晶片。這個晶片連線了CPU、記憶體和IO匯流排。

前端匯流排:CPU與北橋連線的匯流排叫做系統匯流排,也叫作前端匯流排。

記憶體匯流排:記憶體與北橋連線的匯流排叫做記憶體匯流排。

由於北橋速度太快,而IO匯流排速度相對北橋顯得太慢,所以北橋和IO匯流排之間,往往要增加一個網橋,叫做南橋,在南橋上一般整合了眾多外設的控制器,比如磁碟控制器、USD控制器等。

匯流排位數:系統匯流排的條數,比如64條或者128條,叫做匯流排的位數。

CPU位數:暫存器和運算單元之間匯流排的條數。

IO匯流排分成資料匯流排、地址匯流排和控制匯流排。定址用地址匯流排,發資料用資料匯流排,發中斷訊號用控制匯流排。IO匯流排是並行而不是序列的。

計算機I/O與I/O模型

通訊

CPU、記憶體和磁碟之間通過網路來通訊,因為主機板上的匯流排很短、很穩定,所以CPU和儲存裝置之間組成的一個“網路”不需要運輸層,而只需要物理層、網路層和上三層的網路。每個IO裝置在啟動時都要向記憶體中對映一個或者多個地址,這個地址有8位長,又被稱做IO埠。針對這個地址的資料,統統被被北橋晶片重定向到匯流排上實際的裝置上。

二、什麼是I/O?

IO是輸入input和輸出output的縮寫,直觀看來就是計算機的輸入輸出,它描述了計算機的資料流動過程,因此IO第一大特徵是有資料的流動。另外,對於一次IO,它究竟是輸入還是輸出,是針對不同的主體而言的,不同的主體有不同的描述。例如,我對你講話,聲音從我這裡產生,我是輸出方,你將聲音傳入大腦解析其內容,你是輸入方。因此,理解IO一定要弄清楚所要研究的主體。

下面從三個層面來理解IO。

  1. 從直觀層面理解IO

    此時,IO是計算機和外設之間的資料流動過程,本體是一個有使用意義的可執行的電腦,它是計算機執行的完全必要部分。姑且認為這個完全必要部分是臺式電腦的主機,裡面有CPU、記憶體、主機板、電源等裝置,因為有了這些,一臺有使用意義的電腦即可執行。有了主機,並不能方便的為人所服務,因此得有外設。外設是電腦的外圍裝置,如顯示器、鍵盤、滑鼠等,它們是完成人機互動的輔助工具。外設包含兩種重要裝置(但不限於此):輸入裝置和輸出裝置。像滑鼠鍵盤屬於輸入裝置,將人的指令轉成“鼠鍵行為”這種資料傳給主機;顯示器是輸出裝置,主機通過運算,把“返回資訊”這種資料傳給顯示器。

  2. 從計算機架構的角度理解IO

    從計算機架構上來講,任何涉及到計算機核心(CPU和記憶體)與其他裝置間的資料轉移的過程就是IO。本體就是計算機核心(CPU和記憶體)。例如從硬碟上讀取資料到記憶體,是一次輸入,將記憶體中的資料寫入到硬碟就產生了輸出。在計算機的世界裡,這就是IO的本質。

  3. 從程式設計的角度理解IO

    此時,IO的主體是其應用程式的執行態,即程式,特別強調的是我們的應用程式其實並不存在實質的IO過程,真正的IO過程是作業系統的事情,這裡把應用程式的IO操作分為兩種動作:IO呼叫和IO執行。IO呼叫是由程式發起,IO執行是作業系統的工作。因此,更準確些來說,此時所說的IO是應用程式對作業系統IO功能的一次觸發,即IO呼叫。

    IO呼叫的目的是將程式的內部資料遷移到外部即輸出,或將外部資料遷移到程式內部即輸入。這裡,外部資料指非程式空間資料,在程式設計時,通常討論的場景是來自外部儲存裝置的資料,如硬碟、CD-ROM、以及需要socket通訊傳輸的網路資料。

    以一個程式的輸入型別的IO呼叫為例,它將完成或引起如下工作內容:

    1. 程式向作業系統請求外部資料

    2. 作業系統將外部資料載入到核心緩衝區

    3. 作業系統將資料從核心緩衝區拷貝到程式緩衝區

    4. 程式讀取資料繼續後面的工作

    從上面的描述來看,我們更容易理解一個IO操作,應用程式和作業系統都幹了些什麼,也幫助我們更容器理解阻塞和非阻塞,非同步和同步的相關IO程式設計概念。

三、I/O模型

在網路環境下,通俗的講,將IO分為兩步:

1.等;

2.資料搬遷。

如果要想提高IO效率,需要將等的時間降低。

五種IO模型包括:阻塞IO、非阻塞IO、訊號驅動IO、IO多路複用、非同步IO。其中,前四個被稱為同步IO。

在講述五種IO模型之前,這裡先對阻塞、非阻塞,以及同步和非同步做一個簡單的說明。

阻塞&非阻塞IO

阻塞和非阻塞強調的是程式對於作業系統IO是否處於就緒狀態的處理方式。

上面已經說過,應用程式的IO實際是分為兩個步驟,IO呼叫和IO執行。IO呼叫是由程式發起,IO執行是作業系統的工作。作業系統的IO情況決定了程式IO呼叫是否能夠得到立即響應。如程式發起了讀取資料的IO呼叫,作業系統需要將外部資料拷貝到程式緩衝區,在有資料拷貝到程式緩衝區前,程式緩衝區處於不可讀狀態,我們稱之為作業系統IO未就緒。

程式的IO呼叫是否能得到立即執行是需要作業系統IO處於就緒狀態的,對於讀取資料的操作,如果作業系統IO處於未就緒狀態,當前程式或執行緒如果一直等待直到其就緒,該種IO方式為阻塞IO。如果程式或執行緒並不一直等待其就緒,而是可以做其他事情,這種方式為非阻塞IO。所以對於非阻塞IO,我們程式設計時需要經常去輪詢就緒狀態。

非同步和同步IO

我們經常會談及同步IO和非同步IO。同步和非同步描述的是針對當前執行執行緒、或程式而言,發起IO呼叫後,當前執行緒或程式是否掛起等待作業系統的IO執行完成。

我們說一個IO執行是同步執行的,意思是程式發起IO呼叫,當前執行緒或程式需要等待作業系統完成IO工作並告知程式已經完成,執行緒或程式才能繼續往下執行其他既定指令。

如果說一個IO執行是非同步的,意思是該動作是由當前執行緒或程式請求發起,且當前執行緒或程式不必等待作業系統IO的執行完畢,可直接繼續往下執行其他既定指令。作業系統完成IO後,當前執行緒或程式會得到作業系統的通知。

以一個讀取資料的IO操作而言,在作業系統將外部資料寫入程式緩衝區這個期間,程式或執行緒掛起等待作業系統IO執行完成的話,這種IO執行策略就為同步,如果程式或執行緒並不掛起而是繼續工作,這種IO執行策略便為非同步。

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

“阻塞”與"非阻塞"與"同步"與“非同步"不能簡單的從字面理解,這裡從分散式系統角度來看:

同步與非同步

同步和非同步關注的是訊息通訊機制

同步,就是在發出一個呼叫時,在沒有得到結果之前,該呼叫就不返回。但是一旦呼叫返回,就得到返回值了。換句話說,就是由呼叫者主動等待這個呼叫的結果。

而非同步則是相反,呼叫在發出之後,這個呼叫就直接返回了,所以沒有返回結果。換句話說,當一個非同步過程呼叫發出後,呼叫者不會立刻得到結果。而是在呼叫發出後,被呼叫者通過狀態、通知來通知呼叫者,或通過回撥函式處理這個呼叫。

如果呼叫方只要有需要,就會傳送請求,不管上次請求有沒有得到被呼叫方應答。而被呼叫方只要呼叫方有請求就會接受,不是等這次請求處理完畢再接受呼叫方新請求。這樣請求應答分開的序列,就可以認為是非同步。非同步情況下,請求和應答不需要一致進行,可能呼叫方後請求的業務,卻先得到被呼叫方的應答。同步是線性的,而非同步可以認為是併發的。

舉個通俗的例子:
你打電話問書店老闆有沒有《xxx》這本書,如果是同步通訊機制,書店老闆會說,你稍等,”我查一下",然後開始查啊查,等查好了(可能是5秒,也可能是一天)告訴你結果(返回結果)。
而非同步通訊機制,書店老闆直接告訴你我查一下啊,查好了打電話給你,然後直接掛電話了(不返回結果)。然後查好了,他會主動打電話給你。在這裡老闆通過“回電”這種方式來回撥。

阻塞與非阻塞

阻塞和非阻塞關注的是程式在等待呼叫結果(訊息,返回值)時的狀態.

阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起。呼叫執行緒只有在得到結果之後才會返回。
非阻塞呼叫指在不能立刻得到結果之前,該呼叫不會阻塞當前執行緒。

還是上面的例子,
你打電話問書店老闆有沒有《xxx》這本書,你如果是阻塞式呼叫,你會一直把自己“掛起”,直到得到這本書有沒有的結果,如果是非阻塞式呼叫,你不管老闆有沒有告訴你,你自己先一邊去玩了, 當然你也要偶爾過幾分鐘check一下老闆有沒有返回結果。
在這裡阻塞與非阻塞與是否同步非同步無關。跟老闆通過什麼方式回答你結果無關。

五種IO模型

這五種模型的簡單對比如下:

  • 阻塞IO就是那種recv, read,一直等,等到有了資料才返回;

  • 非阻塞IO就是立即返回,設定描述符為非阻塞,但是要程式自己一直檢查是否可讀;

  • IO複用其實也是阻塞的,不過可以用來等很多描述符,比起阻塞有了進步,可以算有點非同步了,但需要阻塞著檢查是否可讀。對同一個描述符的IO操作也是有序的。

  • 訊號驅動IO採用訊號機制等待,有了更多的進步,不用監視描述符了,而且不用阻塞著等待資料到來,被動等待訊號通知,由訊號處理程式處理。但對同一個描述符的IO操作還是有序的。

  • 非同步IO,傳送IO請求後,不用等了,也不再需要傳送IO請求獲取結果了。等到通知後,其實是系統幫你把資料讀取好了的,你等到的通知也不再是要求你去讀寫IO了,而是告訴你IO請求過程已經結束了。你要做的就是可以處理資料了。且同一個描述符上可能同時存在很多請求。(對應上面那個買書例子中,就是送書到我家,我直接看書就行了,不需要再去跑一趟了)。

在介紹五種IO模型時,我會舉生活中釣魚的例子,加深理解。

  1. 阻塞IO(blocking I/O)

    在核心將資料準備好之前,系統呼叫會一直等待所有的套接字,預設的是阻塞方式。

    釣魚的例子:A拿著一支魚竿在河邊釣魚,並且一直在魚竿前等,在等的時候不做其他的事情,十分專心。只有魚上鉤的時,才結束掉等的動作,把魚釣上來。

    其實,我們例子中所說的魚竿就是這一個檔案描述符。這個模型是我們最常見的,程式呼叫和我們編寫的基本程式是一致的。程式後面的操作必須在之前的操作之後執行,當之前的操作阻塞住了,後面操作就不能執行下去,一直處於等待狀態。

    計算機I/O與I/O模型

  2. 非阻塞IO(noblocking I/O)

    B也在河邊釣魚,但是B不想將自己的所有時間都花費在釣魚上,在等魚上鉤這個時間段中,B也在做其他的事情(一會看看書,一會讀讀報紙,一會又去看其他人的釣魚等),但B在做這些事情的時候,每隔一個固定的時間檢查魚是否上鉤。一旦檢查到有魚上鉤,就停下手中的事情,把魚釣上來。

    其實,B在檢查魚竿是否有魚,是一個輪詢的過程。

    每次客戶詢問核心是否有資料準備好,即檔案描述符緩衝區是否就緒。當有資料包準備好時,就進行拷貝資料包的操作。當沒有資料包準備好時,也不阻塞程式,核心直接返回未準備就緒的訊號,等待使用者程式的下一個輪尋。

    但是,輪尋對於CPU來說是較大的浪費,一般只有在特定的場景下才使用 。

    計算機I/O與I/O模型

  3. 訊號驅動IO(signal blocking I/O)

    C也在河邊釣魚,但與A、B不同的是,C比較聰明,他給魚竿上掛一個鈴鐺,當有魚上鉤的時候,這個鈴鐺就會被碰響,C就會將魚釣上來。

    訊號驅動IO模型,應用程式告訴核心:當資料包準備好的時候,給我傳送一個訊號,對SIGIO訊號進行捕捉,並且呼叫我的訊號處理函式來獲取資料包。

    訊號驅動IO在實際中並不常用。

  4. IO多路複用(I/O multiplexing)

    D同樣也在河邊釣魚,但是D生活水平比較好,D拿了很多的魚竿,一次性有很多魚竿在等,D不斷的檢視每個魚竿是否有魚上鉤。增加了效率,減少了等待的時間。

    IO多路轉接是多了一個select函式,select函式有一個引數是檔案描述符集合,對這些檔案描述符進行迴圈監聽,當某個檔案描述符就緒時,就對這個檔案描述符進行處理。

    其中,select只負責等,recvfrom只負責拷貝。

    IO多路轉接是屬於阻塞IO,但可以對多個檔案描述符進行阻塞監聽,所以效率較阻塞IO的高。

    計算機I/O與I/O模型

  5. 非同步IO(asynchronous I/O)

    E也想釣魚,但E有事情,於是他僱來了F,讓F幫他等待魚上鉤,一旦有魚上鉤,F就打電話給E,E就會將魚釣上去。當應用程式呼叫aio_read時,核心一方面去取資料包內容返回,另一方面將程式控制權還給應用程式,應用程式繼續處理其他事情,是一種非阻塞的狀態。

    當核心中有資料包就緒時,由核心將資料包拷貝到應用程式中,返回aio_read中定義好的函式處理程式。

    很少有Linux系統支援,Windows的IOCP就是該模型。

    計算機I/O與I/O模型

總結:從下圖可以看出,阻塞程度:阻塞IO>非阻塞IO>多路轉接IO>訊號驅動IO>非同步IO,效率是由低到高的。

計算機I/O與I/O模型

四、參考

blog.csdn.net/ZWE7616175/…

www.cnblogs.com/findumars/p…

www.jianshu.com/p/fa7bdc4f3…



相關文章