select用法&原理詳解(源碼剖析)(轉)


今天遇到了在select()前后fd_set的變化問題,查了好久終於找到一個有用的帖子了,很贊,很詳細!!原文鏈接如下:

select用法&原理詳解(源碼剖析)

我的問題是:

如下圖示:在select()函數前后分別打印fdsread和fdsreaduse兩個fd_set,

在gjm06-1和gjm06-2之所以一樣是因為在打印前我令 fdsreaduse = fdsread; ,然后打印后,執行 selet(maxfd,&fdsreaduse,NULL,NULL,NULL); ,再分別打印兩個fd_set。

我的疑惑是:前后兩次fdsread都是一樣的,為什么fdsreaduse打印出來就不是一樣的了。

通過查看代碼,發現了兩次打印中間只有一個select()函數對fdsreaduse進行了操作。於是上網搜索,才明白select模型(見下摘錄)。

 【注】

sockfd = 4

remotefd = 5

devfd = 6(devicefd)

 

 

我將我所需要的部分,摘錄如下:

簡單理解select模型

理解select模型的關鍵在於理解fd_set,為說明方便,取fd_set長度為1字節,fd_set中的每一bit可以對應一個文件描述符fd。則1字節長的fd_set最大可以對應8個fd。

(1)執行fd_set set; FD_ZERO(&set);則set用位表示是0000,0000。

(2)若fd=5,執行FD_SET(fd,&set);后set變為0001,0000(第5位置為1)

(3)若再加入fd=2,fd=1,則set變為0001,0011

(4)執行select(6,&set,0,0,0)阻塞等待

(5)若fd=1,fd=2上都發生可讀事件,則select返回,此時set變為0000,0011。注意:沒有事件發生的fd=5被清空。

所以,我們可以得到select模型的特點:
(1) 文件描述符個數有限,一般來說這個數目和系統內存關系很大。select使用位域的方式來傳遞關心的文件描述符,位域就有最大長度。select使用位域的方式傳回就緒的文件描述符,調用者需要循環遍歷每一個位判斷是否就緒,當文件描述符個數很多,但是空閑的文件描述符大大多於就緒的文件描述符的時候,效率很低。

(2) 將fd加入select監控集的同時,還要再使用一個數據結構array保存放到select監控集中的fd,一是用於再select 返回后,array作為源數據和fd_set進行FD_ISSET判斷。二是select返回后會把以前加入的但並無事件發生的fd清空,則每次開始 select前都要重新從array取得fd逐一加入(FD_ZERO最先),掃描array的同時取得fd最大值maxfd,用於select的第一個 參數。

(3) 可見select模型必須在select前循環array(加fd,取maxfd),select返回后循環array(FD_ISSET判斷是否有時間發生)。

 

通過以上第(5)條性質,可知:在select()時,只對devfd=6進行了write/read,故remotefd和socket都被清空了。

至此我的問題已解決。

-----------------------------------------------------------------分  割  線--------------------------------------------------------------------

此外,還有一個經典的select原理圖,放在這里以便大家理解:

注:select 原理圖,摘自 IBM iSeries 信息中心

 

 

 既然到這里了,就看一下select函數的原型吧

select函數原型:

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
 
/*參數列表
int maxfdp是一個整數值,是指集合中所有文件描述符的范圍,即所有文件描述符的最大值加1,不能錯!在Windows中這個參數的值無所謂,可以設置不正確。 
  
fd_set *readfds是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的讀變化的,即我們關心是否可以從這些文件中讀取數據了,
如果這個集合中有一個文件可讀,select就會返回一個大於0的值,表示有文件可讀,如果沒有可讀的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,
select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何文件的讀變化。    fd_set *writefds是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的寫變化的,即我們關心是否可以向這些文件中寫入數據了,
如果這個集合中有一個文件可寫,select就會返回一個大於0的值,表示有文件可寫,如果沒有可寫的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,
select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何文件的寫變化。    fd_set *errorfds同上面兩個參數的意圖,用來監視文件錯誤異常。    struct timeval* timeout是select的超時時間,這個參數至關重要,它可以使select處於三種狀態: 第一,若將NULL以形參傳入,即不傳入時間結構,就是將select置於阻塞狀態,一定等到監視文件描述符集合中某個文件描述符發生變化為止; 第二,若將時間值設為0秒0毫秒,就變成一個純粹的非阻塞函數,不管文件描述符是否有變化,都立刻返回繼續執行,文件無變化返回0,有變化返回一個正值; 第三,timeout的值大於0,這就是等待的超時時間,即 select在timeout時間內阻塞,超時時間之內有事件到來就返回了,否則在超時后不管怎樣一定返回,返回值同上述。
*/

頭文件:

select位於: #include <sys/select.h> 

struct timeval位於: #include <sys/time.h> 

返回值:

負值:select錯誤
正值:某些文件可讀寫或出錯
0:等待超時,沒有可讀寫或錯誤的文件

 參數timeout為結構timeval,用來設置select()的等待時間,其結構定義如下:

struct timeval
{
    time_t tv_sec;//second
    time_t tv_usec;//minisecond
};

 

 

 

over。。。

 

參考:

本人牆推: Linux中select IO復用機制&使用代碼實例

select處理帶外數據&解決socket中多用戶問題代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM