1. 程式人生 > >深入理解select網路模型(linux/windows)

深入理解select網路模型(linux/windows)

IO模型主要分為以下幾種

(1)阻塞I/O模型

(2)非阻塞IO模型

(3)IO複用模型(select 、poll)

(4)訊號驅動式IO模型

(5)非同步IO模型

select模型屬於IO複用模型,所謂的IO複用就是核心一旦發現程序指定的一個或多個IO就緒,它就通知程序,讓程序去完成IO操作。

在select模型中,我們會阻塞於select呼叫,直到呼叫超時或者套接字變為可讀它才返回。

由於select對比於普通IO模型,需要兩個系統呼叫,在效率上還略有劣勢。但是他真正的好處在於,可以等待多個描述符就緒。

與IO複用密切相關的另一種模型就是在多執行緒中去使用阻塞式IO,這種模型和IO複用很類似。

select函式的原型:int select(int maxfdpl,fd_set* readset,fd_set* writeset,fd_set* exceptset,const struct timeval* timeout);

我們將通過對函式傳參,告訴核心對那些描述符感興趣,在linux中,不僅僅侷限於套接字,任何描述符都可以。

關於timeval這個結構體,它是用來設定阻塞時間的,經過測試,這個阻塞的時間是整體的阻塞時間,而不是集合內的單個套接字的阻塞時間。

比如,我設定阻塞時間為10s,那麼連線了一個客戶端也是阻塞10s,連線5個客戶端,也還是阻塞10s。

這個時間引數,存在三種可能

(1)傳遞一個空指標,讓它一直阻塞。

(2)傳遞普通指定的時間。

(3)傳遞時間引數被設定為0,即不等待。但是,經過我的測試·,雖然說時間設定為0,但是當有套接字變為可讀時它還是可以正常檢測出來。

本人推測,當為0時,系統只會輪詢一遍,當有套接字可讀寫時,立即返回。如果,設定了指定時間,它就會在這個時間段裡,一直反覆的

輪詢,直到輪詢到第一個可讀寫的套接字,然後立即返回。注意,是輪詢到第一個就返回,不是說一直輪詢到指定時間結束,然後把所有可

讀寫的套接字一起在集合內返回。

所有感興趣的套接字都要先新增到fd_set這個集合中,關於這個集合的是怎麼回事,普遍推論是它是一個整數陣列,每個整數都是32位整數。

那麼,每個描述符都可以在數組裡找到指定位。什麼意思呢?就是陣列中第一個元素是32為的,那它就可以表示0~31這個區間內的描述符,

然後第二個整數元素,可以表示32~63,以此類推。

當有套接字進入可讀寫狀態時,那麼它所在的位,就會被置位,然後我們通過FD_ISSET巨集,就可以檢測出哪一位被置位了,就可以得到對應

的套接字。

對於maxfdpl這個引數,它是待測試的描述符個數,在數值上等於被等待的最大的描述符的值加1.為什麼要指定它呢?前面提出過了,fd_set

是一個數組,每一位對應這描述符的值,假設最大的描述符是5,那麼我們只需要讓系統輪詢前6位就好了,後面的位就可以不要浪費時間去

輪詢了,因為根本就沒有用到,所以我們把5加1作為引數傳遞給select函式。當然,這個引數只有伯克利套接字對它感興趣,在windows中

·MSDN指出它是可以被忽略的。以上的推測,也僅限於linux的實現,對於windows並不適用。

對於windows

typedef struct fd_set {
       u_int fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

這是他的fd_set的實現,它是維護了一個socket的陣列,將socket新增進數組裡,我想這也是為什麼他保留了第一個引數,但是並不對它感興趣的原因。

select等待的最大描述符數是有限制的,在windows中是64.而linux是1024.

對於linux,當描述符數超過最大時,我在網上找到了一個例項,即便每個select檢查的描述符數,小於1024,但是整個程序檢查的描述符數

超過了1024也不行。select 的正確語義應該時,整個程序中,select 最多可以檢查1024個描述符,而不管用了多少 select 函式。

對於大於1024的描述符,FD_SET的處理方式是拿描述符對1024取模。假設描述符為1029,FD_SET(1029,&set)相當於FD_SET(5,&set),

而 5 可能不是一個有效的描述符,當 select 去檢查描述符 5 時,因為 5 不是有效的描述符,所以出 Invalid argument 或 Bad file descriptor 就比較合理了。

那麼如何超出這個最大的限制呢,僅僅通過修改FD_SETSIZE巨集的大小,對於linux通常是行不通的。對於windows。。。。。我也不瞭解。