網絡IO模型

IO有兩種操做,同步IO和異步IO。同步IO指的是,必須等待IO操做完成後,控制權才返回給用戶進程。異步IO指的是,無須等待IO操做完成,就將控制權返回給用戶進程。編程

網絡中的IO,因爲不一樣的IO設備有着不一樣的特色,網絡通訊中每每須要等待。常見的有如下4種狀況。數組

(1)輸入操做:等待數據到達套接字接收緩衝區。服務器

(2)輸出操做:等待套接字發送緩衝區有足夠的空間容納將要發送的數據。網絡

(3)服務器接收鏈接請求:等待新的客戶端鏈接請求的到來。異步

(4)客戶端發送鏈接請求:等待服務器會送你個客戶的發起的SYN所對應的ACK。socket

當一個網絡IO(假設是read)發生時,它會涉及兩個系統對象,一個是調用這個IO的進程,另外一個是系統內核。當一個read操做發生時,它會經歷兩個階段:(1)等待數據準備;(2)將數據從內核拷貝到進程中。函數

 

4種網絡IO模型線程

(1)阻塞IO模型(2)非阻塞IO模型(3)多路IO複用模型(4)異步IO模型3d

 

1.阻塞IO模型對象

在Linux中,默認狀況下全部的socket都是阻塞的,一個典型的讀寫流程以下圖:

阻塞和非阻塞的概念描述的是用戶線程調用內核IO操做的方式:阻塞是指IO操做須要完全完成後才返回用戶空間;而非阻塞是指IO操做被調用後當即返回給用戶一個狀態值,不須要等到IO操做完全完成。

 

 2.非阻塞IO模型

在Linux下,能夠經過設置socket使IO變爲非阻塞狀態。當對一個非阻塞的socket執行read操做時,流程以下圖

 

 

3.多路IO複用模型

多路IO複用,有時也稱爲事件驅動IO。它的基本原理就是有個函數(如select)會不斷地輪詢所負責的socket,當某個socket有數據到達了,就通知用戶進程,多路IO複用模型的流程圖以下:

 

 

select、poll和epoll的區別

select、poll和epoll都是多路IO複用的機制。多路IO複用就經過一種機制,能夠監視多個描述符,一旦某個描述符就緒(通常是讀就緒或者寫就緒),可以通知程序進行相應的讀寫操做。但select、poll和epoll本質上都是同步IO,由於它們都須要在讀寫事件就緒後本身負責進行讀寫,便是阻塞的,而異步IO則無須本身負責進行讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間。

下面對這3種多路IO複用進行對比。

(1) 首先仍是來看常見的select()和poll()。對於網絡編程來講,通常認爲poll比select要高級一些,這主要源於如下幾個緣由。

1)poll()不要求開發者在計算最大文件描述符時進行+1的操做。

2)poll()在應付大數目的文件描述符的時候速度更快,由於對select()來講內核須要檢查大量描述符對應的fd_set中的每個比特位,比較費時。

3)select()能夠監控的文件描述符數目是固定的,相對來講也較少(1024或2048)。若是須要監控數值比較大的文件描述符,或是分佈得很稀疏的較少的描述符,效率也會很低。而對於poll()函數來講,就能夠建立特定大小的數組來保存監控的描述符,而不受文件描述符值大小的影響,並且poll()能夠監控的文件數目遠大於select()。

4)對於select()來講,所監控的fd_set在select返回以後會發生變化,因此在下一次進入select()以前都須要從新初始化須要監控的fd_set,poll()函數將監控的輸入和輸出事件分開,容許被監控的文件數組被複用而不須要從新初始化。

5)select()函數的超時參數在返回時也是未定義的,考慮到可移植性,每次在超時以後在下一次進入到select()以前都須要從新設置超時參數。

(2)select()的優勢以下所述。

1)select的可移植性更好,在某些UNIX系統上不支持poll().

2)select()對於超時值提供了更好的精度,而poll()是精度較差。

(3)epoll的優勢以下所述。

1)支持一個進程打開大數目的socket描述符(FD)

select()最不能忍受的是一個進程所打開的FD是有必定限制的,由FD_SETSIZE的默認值是1024/2048。對於那些須要支持上萬鏈接數目的IM服務器來講顯然太少了。這時候能夠選擇修改這個宏而後從新編譯內核。不過epoll則沒有這個限制,它所支持的FD上限是最大能夠打開文件的數目,這個數字通常大於2048.舉個例子,在1GB內存的空間中這個數字通常是10萬左右,具體數目可使用cat/proc/sys/fs/file-max查看,通常來講這個數目和系統內存關係很大。

2)IO效率不隨FD數目增長而線性降低。

傳統的select/poll另外一個致命弱點就是當你擁有一個很大的socket集合,不過因爲網絡延遲,任一時間只有部分的socket是「活躍」的,可是selecct/poll每次調用都會線性掃描所有的集合,致使效率呈現線性降低。可是epoll不存在這個問題,它只會對「活躍」的socket進行操做——這是由於在內核中實現epoll是根據每一個fd上面的callback函數實現的。那麼,只有「活躍」的socket纔會主動去調用callback函數,其餘idle狀態socket則不會,在這個點上,epoll實現了一個「僞」AIO,由於這時候推進力由Linux內核提供。

3)使用mmap加速內核與用戶空間的消息傳遞

這點實際上涉及epoll的具體實現。不管是selecct、poll仍是epoll都須要內核把fd消息通知給用戶空間,如何避免沒必要要的內存拷貝就顯得尤其重要。在這點上,epoll是經過內核與用戶空間mmap處於同一塊內存實現的。

對於poll來講須要將用戶傳入的pollfd數組拷貝到內核空間,由於拷貝操做和數組長度相關,時間上來看,這是一個O(n)操做,當事情發生後,poll將得到的數據傳送到用戶空間,並執行釋放內存和剝離等待隊列等工做,向用戶空間拷貝數據與剝離等待隊列等操做的時間複雜度一樣是O(n)。

相關文章
相關標籤/搜索