网络IO模型
IO有兩種操作,同步IO和異步IO。同步IO指的是,必須等待IO操作完成后,控制權才返回給用戶進程。異步IO指的是,無須等待IO操作完成,就將控制權返回給用戶進程。
網絡中的IO,由于不同的IO設備有著不同的特點,網絡通信中往往需要等待。常見的有以下4種情況。
(1)輸入操作:等待數據到達套接字接收緩沖區。
(2)輸出操作:等待套接字發送緩沖區有足夠的空間容納將要發送的數據。
(3)服務器接收連接請求:等待新的客戶端連接請求的到來。
(4)客戶端發送連接請求:等待服務器會送你個客戶的發起的SYN所對應的ACK。
當一個網絡IO(假設是read)發生時,它會涉及兩個系統對象,一個是調用這個IO的進程,另一個是系統內核。當一個read操作發生時,它會經歷兩個階段:(1)等待數據準備;(2)將數據從內核拷貝到進程中。
?
4種網絡IO模型
(1)阻塞IO模型(2)非阻塞IO模型(3)多路IO復用模型(4)異步IO模型
?
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)。
?
轉載于:https://www.cnblogs.com/wuyepeng/p/10167751.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
- 上一篇: windows下多进程加协程并发模式
- 下一篇: Parallel学习