設計極其糟糕的select函式
相較windows而言,大部分unix api函式設計都比較考究,但也有少數簡直就是奇葩,select函式正是這些奇葩中非常燦爛的一朵。我原來一致鍾情於ace,接觸的只是reactor,最近由於開始自己設計網路層的類庫,被迫和select打了一些交道,被迫和這個函式打了一些交道,結果只能是看著就吐了,吐著吐著就習慣了。
unix下select這個api由主函式select和幾個fd_set輔助函式構成。如下:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void fd_clr(int fd, fd_set *set);
int fd_isset(int fd, fd_set *set);
void fd_set(int fd, fd_set *set);
void fd_zero(fd_set *set);
//fd_set的實際結構是__kernel_fd_set
typedef struct __kernel_fd_set;
select主函式的第乙個引數nfds的描述是 nfds is the highest-numbered file descriptor in any of the three sets, plus 1.也就是最大的檔案控制代碼數值+1,這個引數的本質意圖應該是加快內部處理的,避免內部處理幾個fd_set的時候都不用處理次數,從而降低系統的計算銷毀,但是select本來就是要處理3個fd_set,這個nfds就只能是這3個fd_set中間的最大值,而且考慮到nfds不可能一直增加,而不減少,在fd_clr清理掉某個控制代碼後,找乙個新的最大值,必須和3個fd_set打交道。簡直是……,很多人,很多庫的處理fd_clr後,其實會偷懶不減少nfds。這個其實不是大家的錯,而是select設計者的原罪。(當然你也可以像ace那樣包裝一層加快實現)
改良的方案很簡單,考慮到fd_set本來就只是內部的乙個結構,每個結構擁有自己的最大值,是一件非常easy的事情。這樣就可以取消那個討厭的nfds引數。我們後面看windows下的select設計時會繼續討論這個問題。
由於3個fd_set引數都是傳入傳出引數,所以如果是伺服器程式,你老只好在每次呼叫select前保留一次這些控制代碼。
再來看看這個unix大部分設計中fd_set的實現,他就是乙個用bit位標識檔案控制代碼的long陣列。所以他只能處理檔案控制代碼數值小於1024的檔案控制代碼,由於還有其他地方會占用前面的檔案控制代碼id,所以其實unix的select根本無法處理fd_setsize個網路請求。甚至極端情況下你的程式一開始就開啟了1024個檔案,你就別想使用這個可愛的函式,他就沒有這個處理能力……
windows下的select函式基本向unix平台靠齊,但是由於windows下handle(socket)完全不是整數(而是乙個指標),windows的select,可以實際處理fd_setsize個檔案控制代碼(windows下這個引數預設64,可以在保護winsock2.h前面重新定義這個巨集調整)。windows下的select函式就沒有使用第乙個引數,其通過設計fd_set達到了同樣加速的目的。
//windows下的fd_set的定義
typedef struct fd_set fd_set;
最後我們看看fd_isset的設計,大部分select的例子都是使用fd_isset判斷某個控制代碼是否被觸發的,但是真正我們寫伺服器時,如果要老老實實使用fd_isset,方法就只能是自己先儲存一組放入select的控制代碼引數。然後將這些控制代碼取出乙個個和返回的fd_set用fd_isset進行判斷,所以這個成本至少是乙個o(nfds),而在windows下,由於控制代碼id不可能直接對映查詢。查詢效率應該是o(輸入引數fd_set的數量*觸發返回的fd_set的數量)。windows下有沒有快一點的法子呢,有,就是不用fd_isset,直接利用fd_set的結構。處理效率就只需要o(觸發返回的fd_set的數量),當然這又違背了select函式fd_iseet這類函式(巨集)的設計初衷,不希望你了解fd_set的內部結構。
整體說來,unix下的select函式的設計是完全是結合unix檔案控制代碼的設計進行的,同時考慮了部分加快速度部分的處理,雖然在那個年代,也許有他的苦衷。但無需遮掩,其整體設計是比較失敗的,既沒有效率,也沒有考慮擴充套件性,而windows下select的設計比unix版本高出一節。而如果把epoll的api拿出來比一下,高下立分。
糟糕的設計
不要去弄髒構造器方法!2009 02 24 這是來自自己最近的專案salesys的乙個經驗之一。坦白說,在這個專案,還有以前的幾個專案中糟糕的設計有很多,這個只是其中之一。在salesys的乙個很大的特點就是大部分的模組在開始頁面都有乙個資訊列表,用於顯示這個模組的主要資訊。就像這樣 在我們的 設計...
極其有用的函式
如需使用sleep函式 那麼在程式的最開始加上下面這一行 注意呼叫的時候sleep的s要大寫 private declare sub sleep lib kernel32 byval dwmilliseconds as long 生成乙個指定範圍的隨機數 private function rndz ...
linux socket的select函式例子
使用select函式可以以非阻塞的方式和多個socket通訊。程式只是演示select函式的使用,功能非常簡單,即使某個連線關閉以後也不會修改當前連線數,連線數達到最大值後會終止程式。1.程式使用了乙個陣列fd a,通訊開始後把需要通訊的多個socket描述符都放入此陣列。2.首先生成乙個叫sock...