五種IO模型分析,五種io模型

來源:互聯網
上載者:User

五種IO模型分析,五種io模型

本文目錄:
1. 基礎
2. I/O模型
2.1 Blocking I/O模型
2.2 Non-Blocking I/O模型
2.3 I/O Multiplexing模型
2.4 Signal-driven I/O模型
2.5 Asynchronous I/O模型
2.6 同步IO和非同步IO、阻塞和非阻塞的區分
3. select()、poll()和epoll
3.1 select() & poll()
3.2 epoll

1. 基礎

在引入IO模型前,先對io等待時某一段資料的"經曆"做一番解釋。

當某個程式或已存在的進程/線程(後文將不加區分的只認為是進程)需要某段資料時,它只能在使用者空間中屬於它自己的記憶體中訪問、修改,這段記憶體暫且稱之為app buffer。假設需要的資料在磁碟上,那麼進程首先得發起相關係統調用,通知核心去載入磁碟上的檔案。但正常情況下,資料只能載入到核心的緩衝區,暫且稱之為kernel buffer。資料載入到kernel buffer之後,還需將資料複製到app buffer。到了這裡,進程就可以對資料進行訪問、修改了。

現在有幾個需要說明的問題。

(1).為什麼不能直接將資料載入到app buffer呢

實際上是可以的,有些程式或者硬體為了提高效率和效能,可以實現核心旁路的功能,避過核心的參與,直接在存放裝置和app buffer之間進行資料轉送,例如RDMA技術就需要實現這樣的核心旁路功能。

但是,最普通也是絕大多數的情況下,為了安全和穩定性,資料必須先拷入核心空間的kernel buffer,再複製到app buffer,以防止進程串進核心空間進行破壞。

(2).上面提到的資料幾次拷貝過程,拷貝方式是一樣的嗎

不一樣。現在的存放裝置(包括網卡)基本上都支援DMA操作。什麼是DMA(direct memory access,直接記憶體存取)?簡單地說,就是記憶體和裝置之間的資料互動可以直接傳輸,不再需要電腦的CPU參與,而是通過硬體上的晶片(可以簡單地認為是一個小cpu)進行控制。

假設,存放裝置不支援DMA,那麼資料在記憶體和存放裝置之間的傳輸,必須通過電腦的CPU計算從哪個地址中擷取資料、拷入到對方的哪些地址、拷入多少資料(多少個資料區塊、資料區塊在哪裡)等等,僅僅完成一次資料轉送,CPU都要做很多事情。而DMA就釋放了電腦的CPU,讓它可以去處理其他任務。

再說kernel buffer和app buffer之間的複製方式,這是兩段記憶體空間的資料轉送,只能由CPU來控制。

所以,在載入硬碟資料到kernel buffer的過程是DMA拷貝方式,而從kernel buffer到app buffer的過程是CPU參與的拷貝方式。

(3).如果資料要通過TCP串連傳輸出去要怎麼辦

例如,web服務對用戶端的響應資料,需要通過TCP串連傳輸給用戶端。

TCP/IP協議棧維護著兩個緩衝區:send buffer和recv buffer,它們合稱為socket buffer。需要通過TCP串連傳輸出去的資料,需要先複製到send buffer,再複製給網卡通過網路傳輸出去。如果通過TCP串連接收到資料,資料首先通過網卡進入recv buffer,再被複製到使用者空間的app buffer。

同樣,在資料複製到send buffer或從recv buffer複製到app buffer時,是CPU參與的拷貝。從send buffer複製到網卡或從網卡複製到recv buffer時,是DMA操作方式的拷貝。

如所示,是通過TCP串連傳輸資料時的過程。

(4).網路資料一定要從kernel buffer複製到app buffer再複製到send buffer嗎

不是。如果進程不需要修改資料,就直接發送給TCP串連的另一端,可以不用從kernel buffer複製到app buffer,而是直接複製到send buffer。這就是零複製技術。

例如httpd不需要訪問和修改任何資訊時,將資料原原本本地複製到app buffer再原原本本地複製到send buffer然後傳輸出去,但實際上複製到app buffer的過程是可以省略的。使用零複製技術,就可以減少一次拷貝過程,提升效率。

當然,實現零複製技術的方法有多種,見我的另一篇結束零複製的文章:零複製(zero copy)技術。

以下是以httpd進程處理檔案類請求時比較完整的資料操作流程。

大致解釋下:用戶端發起對某個檔案的請求,通過TCP串連,請求資料進入TCP 的recv buffer,再通過recv()函數將資料讀入到app buffer,此時httpd背景工作處理序對資料進行一番解析,知道請求的是某個檔案,於是發起某個系統調用(例如要讀取這個檔案,發起read()),於是核心載入該檔案,資料從磁碟複製到kernel buffer再複製到app buffer,此時httpd就要開始構建響應資料了,可能會對資料進行一番修改,例如在響應首部中加一個欄位,最後將修改或未修改的資料複製(例如send()函數)到send buffer中,再通過TCP串連傳輸給用戶端。

2. I/O模型

所謂的IO模型,描述的是出現I/O等待時進程的狀態以及處理資料的方式。圍繞著進程的狀態、資料準備到kernel buffer再到app buffer的兩個階段展開。其中資料複製到kernel buffer的過程稱為資料準備階段,資料從kernel buffer複製到app buffer的過程稱為資料複製階段。請記住這兩個概念,後面描述I/O模型時會一直用這兩個概念。

本文以httpd進程的TCP串連方式處理本地檔案為例,請無視httpd是否真的實現了如此、那般的功能,也請無視TCP串連處理資料的細節,這裡僅僅只是作為方便解釋的樣本而已。另外,本文用本地檔案作為I/O模型的對象不是很適合,它的重頭戲是在通訊端上,如果想要看處理TCP/UDP過程中通訊端的I/O模型,請看完此文後,再結合我的另一篇文章"不可不知的socket和TCP串連過程"以重新認識I/O模型。

再次說明,從硬體裝置到記憶體的資料轉送過程是不需要CPU參與的,而記憶體間傳輸資料是需要CPU參與的。

2.1 Blocking I/O模型

假設用戶端發起index.html的檔案請求,httpd需要將index.html的資料從磁碟中載入到自己的httpd app buffer中,然後複製到send buffer中發送出去。

但是在httpd想要載入index.html時,它首先檢查自己的app buffer中是否有index.html對應的資料,沒有就發起系統調用讓核心去載入資料,例如read(),核心會先檢查自己的kernel buffer中是否有index.html對應的資料,如果沒有,則從磁碟中載入,然後將資料準備到kernel buffer,再複製到app buffer中,最後被httpd進程處理。

如果使用Blocking I/O模型:

(1).當設定為blocking i/o模型,httpd從到都是被阻塞的。
(2).只有當資料複製到app buffer完成後,或者發生了錯誤,httpd才被喚醒處理它app buffer中的資料。
(3).cpu會經過兩次環境切換:使用者空間到核心空間再到使用者空間。
(4).由於階段的拷貝是不需要CPU參與的,所以在階段準備資料的過程中,cpu可以去處理其它進程的任務。
(5).階段的資料複製需要CPU參與,將httpd阻塞,在某種程度上來說,有助於提升它的拷貝速度。
(6).這是最省事、最簡單的IO模式。

如:

2.1 Non-Blocking I/O模型

(1).當設定為non-blocking時,httpd第一次發起系統調用(如read())後,立即返回一個錯誤值EWOULDBLOCK(至於read()讀取一個普通檔案時能否返回EWOULDBLOCK請無視,畢竟I/O模型主要是針對通訊端檔案的,就當read()是recv()好了),而不是讓httpd進入睡眠狀態。UNP中也正是這麼描述的。

When we set a socket to be nonblocking, we are telling the kernel "when an I/O operation that I request cannot be completed without putting the process to sleep, do not put the process to sleep, but return an error instead.

(2).雖然read()立即返回了,但httpd還要不斷地去發送read()檢查核心:資料是否已經成功拷貝到kernel buffer了?這稱為輪詢(polling)。每次輪詢時,只要核心沒有把資料準備好,read()就返回錯誤資訊EWOULDBLOCK。
(3).直到kernel buffer中資料準備完成,再去輪詢時不再返回EWOULDBLOCK,而是將httpd阻塞,以等待資料複製到app buffer。
(4).httpd在到階段不被阻塞,但是會不斷去發送read()輪詢。在被阻塞,將cpu交給核心把資料copy到app buffer。

如:

2.3 I/O Multiplexing模型

稱為多路IO模型或IO複用,意思是可以檢查多個IO等待的狀態。有三種IO複用模型:select、poll和epoll。其實它們都是一種函數,用於監控指定檔案描述符的資料是否就緒,就緒指的是對某個系統調用不再阻塞了,例如對於read()來說,就是資料準備好了就是就緒狀態。就緒種類包括是否可讀、是否可寫以及是否異常,其中可讀條件中就包括了資料是否準備好。當就緒之後,將通知進程,進程再發送對資料操作的系統調用,如read()。所以,這三個函數僅僅只是處理了資料是否準備好以及如何通知進程的問題。可以將這幾個函數結合阻塞和非阻塞IO模式使用,例如設定為非阻塞時,select()/poll()/epoll將不會阻塞在對應的描述符上,調用函數的進程/線程也就不會被阻塞。

select()和poll()差不多,它們的監控和通知手段是一樣的,只不過poll()要更聰明一點,所以此處僅以select()監控單個檔案請求為例簡單介紹IO複用,至於更具體的、監控多個檔案以及epoll的方式,在本文的最後專門解釋。

(1).當想要載入某個檔案時,假如httpd要發起read()系統調用,如果是阻塞或者非阻塞情形,那麼read()會根據資料是否準備好而決定是否返回,是否可以主動去監控這個資料是否準備到了kernel buffer中呢,亦或者是否可以監控send buffer中是否有新資料進入呢?這就是select()/poll()/epoll的作用。
(2).當使用select()時,httpd發起一個select調用,然後httpd進程被select()"阻塞"。由於此處假設只監控了一個請求檔案,所以select()會在資料準備到kernel buffer中時直接喚醒httpd進程。之所以阻塞要加上雙引號,是因為select()有時間間隔選項可用控制阻塞時間長度,如果該選項設定為0,則select不阻塞,此時表示立即返回但一直輪詢檢查是否就緒,還可以設定為永久阻塞。
(3).當select()的監控對象就緒時,將通知(輪詢情況)或喚醒(阻塞情況)httpd進程,httpd再發起read()系統調用,此時資料會從kernel buffer複製到app buffer中並read()成功。
(4).httpd發起第二個系統調用(即read())後被阻塞,CPU全部交給核心用來複製資料到app buffer。(5).對於httpd只處理一個串連的情況下,IO複用模型還不如blocking I/O模型,因為它前後發起了兩個系統調用(即select()和read()),甚至在輪詢的情況下會不斷消耗CPU。但是IO複用的優勢就在於能同時監控多個檔案描述符。

更詳細的說明,見本文末。

2.4 Signal-driven I/O模型

即訊號驅動IO模型。當開啟了訊號驅動功能時,首先發起一個訊號處理的系統調用,如sigaction(),這個系統調用會立即返回。但資料在準備好時,會發送SIGIO訊號,進程收到這個訊號就知道資料準備好了,於是發起操作資料的系統調用,如read()。

在發起訊號處理的系統調用後,進程不會被阻塞,但是在read()將資料從kernel buffer複製到app buffer時,進程是被阻塞的。

2.5 Asynchronous I/O模型

即非同步IO模型。當設定為非同步IO模型時,httpd首先發起非同步系統調用(如aio_read(),aio_write()等),並立即返回。這個非同步系統調用告訴核心,不僅要準備好資料,還要把資料複製到app buffer中。

httpd從返回開始,直到資料複製到app buffer結束都不會被阻塞。當資料複製到app buffer結束,將發送一個訊號通知httpd進程。

看上去非同步很好,但是注意,在複製kernel buffer資料到app buffer中時是需要CPU參與的,這意味著不受阻的httpd會和非同步呼叫函數爭用CPU。如果並發量比較大,httpd接入的串連數可能就越多,CPU爭用情況就越嚴重,非同步函數返回成功訊號的速度就越慢。如果不能很好地處理這個問題,非同步IO模型也不一定就好。

2.6 同步IO和非同步IO、阻塞和非阻塞的區分

阻塞、非阻塞、IO複用、訊號驅動都是同步IO模型。因為在發起操作資料的系統調用(如本文的read())過程中是被阻塞的。這裡要注意,雖然在載入資料到kernel buffer的資料準備過程中可能阻塞、可能不阻塞,但kernel buffer才是read()函數的操作對象,同步的意思是讓kernel buffer和app buffer資料同步。顯然,在保持kernel buffer和app buffer同步的過程中,進程必須被阻塞,否則read()就變成非同步read()。

只有非同步IO模型才是非同步,因為發起的非同步類的系統調用(如aio_read())已經不管kernel buffer何時準備好資料了,就像後台一樣read一樣,aio_read()可以一直等待kernel buffer中的資料,在準備好了之後,aio_read()自然就可以將其複製到app buffer。

3 select()、poll()和epoll

前面說了,這三個函數是檔案描述符狀態監控的函數,它們可以監控一系列檔案的一系列事件,當出現滿足條件的事件後,就認為是就緒或者錯誤。事件大致分為3類:可讀事件、可寫事件和例外狀況事件。它們通常都放在迴圈結構中進行迴圈監控。

select()和poll()函數處理方式的本質類似,只不過poll()稍微先進一點,而epoll處理方式就比這兩個函數先進多了。當然,就算是先進分子,在某些情況下效能也不一定就比老傢伙們強。

3.1 select() & poll()

首先,通過FD_SET宏函數建立待監控的描述符集合,並將此描述符集合作為select()函數的參數,可以在指定select()函數阻塞時間間隔,於是select()就建立了一個監控對象。

除了普通檔案描述符,還可以監控通訊端,因為通訊端也是檔案,所以select()也可以監控通訊端檔案描述符,例如recv buffer中是否收到了資料,也即監控通訊端的可讀性,send buffer中是否滿了,也即監控通訊端的可寫性。select()預設最大可監控1024個檔案描述符。而poll()則沒有此限制。

select()的時間間隔參數分3種:
(1).設定為指定時間間隔內阻塞,除非之前有就緒事件發生。
(2).設定為永久阻塞,除非有就緒事件發生。
(3).設定為完全不阻塞,即立即返回。但因為select()通常在迴圈結構中,所以這是輪詢監控的方式。

當建立了監控對象後,由核心監控這些描述符集合,於此同時調用select()的進程被阻塞(或輪詢)。當監控到滿足就緒條件時(監控事件發生),select()將被喚醒(或暫停輪詢),於是select()返回滿足就緒條件的描述符數量,之所以是數量而不僅僅是一個,是因為多個檔案描述符可能在同一時間滿足就緒條件。由於只是返回數量,並沒有返回哪一個或哪幾個檔案描述符,所以通常在使用select()之後,還會在迴圈結構中的if語句中使用宏函數FD_ISSET進行遍曆,直到找出所有的滿足就緒條件的描述符。最後將描述符集合通過指定函數拷貝回使用者空間,以便被進程處理。

監聽描述符集合的大致過程如所示,其中select()只是其中的一個環節:

大概描述下這個迴圈監控的過程:

(1).首先通過FD_ZERO宏函數初始化描述符集合。圖中每個小方格表示一個檔案描述符。
(2).通過FD_SET宏函數建立描述符集合,此時集合中的檔案描述符都被開啟,也就是稍後要被select()監控的對象。
(3).使用select()函數監控描述符集合。當某個檔案描述符滿足就緒條件時,select()函數返回集合中滿足條件的數量。圖中標黃色的小方塊表示滿足就緒條件的描述符。
(4).通過FD_ISSET宏函數遍曆整個描述符集合,並將滿足就緒條件的描述符發送給進程。同時,使用FD_CLR宏函數將滿足就緒條件的描述符從集合中移除。
(5).進入下一個迴圈,繼續使用FD_SET宏函數向描述符集合中添加新的待監控描述符。然後重複(3)、(4)兩個步驟。

如果使用簡單的虛擬碼來描述:

FD_ZEROfor() {    FD_SET()    select()    if(){        FD_ISSET()        FD_CLR()    }    writen()}

以上所說只是一種需要迴圈監控的樣本,具體如何做卻是不一定的。不過從中也能看出這一系列的流程。

3.2 epoll

epoll比poll()、select()先進,考慮以下幾點,自然能看出它的優勢所在:

(1).epoll_create()建立的epoll執行個體可以隨時通過epoll_ctl()來新增和刪除感興趣的檔案描述符,不用再和select()每個迴圈後都要使用FD_SET更新描述符集合的資料結構。
(2).在epoll_create()建立epoll執行個體時,還建立了一個epoll就緒鏈表list。而epoll_ctl()每次向epoll執行個體添加描述符時,還會註冊該描述符的回呼函數。當epoll執行個體中的描述符滿足就緒條件時將觸發回呼函數,被移入到就緒鏈表list中。
(3).當調用epoll_wait()進行監控時,它只需確定就緒鏈表中是否有資料即可,如果有,將複製到使用者空間以被進程處理,如果沒有,它將被阻塞。當然,如果監控的對象設定為非阻塞模式,它將不會被阻塞,而是不斷地去檢查。

也就是說,epoll的處理方式中,根本就無需遍曆描述符集合。

回到Linux系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html回到網站架構系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7576137.html回到資料庫系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7586194.html轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/7624733.html註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支援能激發作者更大的寫作熱情,非常感謝!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.