Linux下select函數(shù)的用法

本文是十多年前利用網(wǎng)上搜集的資料拼接而成,這些內(nèi)容基本上一直也不會過時。
0. 函數(shù)定義
頭文件:
?
函數(shù)定義:
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval * timeout);
函數(shù)說明:
select()用來等待文件描述詞狀態(tài)的改變。參數(shù)n代表最大的文件描述詞加1,參數(shù)readfds、writefds 和exceptfds稱為描述詞組,是用來回傳該描述詞的讀,寫或例外的狀況。
底下的宏提供了處理這三種描述詞組的方式:
FD_CLR(inr fd, fd_set* set); 用來清除描述詞組set中相關(guān)fd的位
FD_ISSET(int fd, fd_set *set); 用來測試描述詞組set中相關(guān)fd的位是否為真
FD_SET(int fd, fd_set *set); 用來設(shè)置描述詞組set中相關(guān)fd的位
FD_ZERO(fd_set *set); 用來清除描述詞組set的全部位
參數(shù):
timeout為結(jié)構(gòu)timeval,用來設(shè)置select()的等待時間,其結(jié)構(gòu)定義如下
返回值:
如果參數(shù)timeout設(shè)為NULL則表示select()沒有timeout。
錯誤代碼:
執(zhí)行成功則返回文件描述詞狀態(tài)已改變的個數(shù),
如果返回0代表在描述詞狀態(tài)改變前已超過timeout時間,
當有錯誤發(fā)生時則返回-1,錯誤原因存于errno,此時參數(shù)readfds,writefds,exceptfds和timeout的值變成不可預(yù)測。
EBADF 文件描述詞為無效的或該文件已關(guān)閉
EINTR 此調(diào)用被信號所中斷
EINVAL 參數(shù)n為負值
ENOMEM 核心內(nèi)存不足
范例:
常見的程序片段:
1. Select函數(shù)詳細介紹
Select在Socket編程中還是比較重要的,可是對于初學Socket的人來說都不太愛用Select寫程序,他們只是習慣寫諸如connect、 accept、recv或recvfrom這樣的阻塞程序(所謂阻塞方式block,顧名思義,就是進程或是線程執(zhí)行到這些函數(shù)時必須等待某個事件的發(fā)生,如果事件沒有發(fā)生,進程或線程就被阻塞,函數(shù)不能立即返回)。
可是使用Select就可以完成非阻塞(所謂非阻塞方式non-block,就是進程或線程執(zhí)行此函數(shù)時不必非要等待事件的發(fā)生,一旦執(zhí)行肯定返回,以返回值的不同來反映函數(shù)的執(zhí)行情況,如果事件發(fā)生則與阻塞方式相同,若事件沒有發(fā)生則返回一個代碼來告知事件未發(fā)生,而進程或線程繼續(xù)執(zhí)行,所以效率較高)方式工作的程序,它能夠監(jiān)視我們需要監(jiān)視的文件描述符的變化情況--讀寫或是異常。
下面詳細介紹一下:
Select的函數(shù)格式(我所說的是Unix系統(tǒng)下的伯克利socket編程,和windows下的有區(qū)別,一會兒說明):
int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
1.1 先說明兩個結(jié)構(gòu)體
一.?struct fd_set
可以理解為一個集合,這個集合中存放的是文件描述符(filedescriptor),即文件句柄,這可以是我們所說的普通意義的文件,當然Unix下任何設(shè)備、管道、FIFO等都是文件形式,全部包括在內(nèi),所以毫無疑問一個socket就是一個文件,socket句柄就是一個文件描述符。
fd_set集合可以通過一些宏由人為來操作,比如:
FD_ZERO(fd_set *);
?清空集合
FD_SET(int, fd_set *);
?將一個給定的文件描述符加入集合之中
FD_CLR(int, fd_set *);
?將一個給定的文件描述符從集合中刪除
檢查集合中指定的文件描述符是否可以讀寫FD_ISSET(int ,fd_set* )
一會兒舉例說明。
二.?struct timeval
是一個大家常用的結(jié)構(gòu),用來代表時間值,有兩個成員,一個是秒數(shù),另一個是毫秒。
1.2 具體解釋select的參數(shù)
int maxfdp:是一個整數(shù)值,是指集合中所有文件描述符的范圍,即所有文件描述符的最大值加1,不能錯!?在Windows中這個參數(shù)的值無所謂,可以設(shè)置不正確。
fd_set* readfds:是指向fd_set結(jié)構(gòu)的指針,這個集合中應(yīng)該包括文件描述符,我們是要監(jiān)視這些文件描述符的讀變化的,即我們關(guān)心是否可以從這些文件中讀取數(shù)據(jù)了,如果這個集合中有一個文件可讀,select就會返回一個大于0的值,表示有文件可讀,如果沒有可讀的文件,則根據(jù)timeout參數(shù)再判斷是否超時,若超出timeout的時間,select返回0,若發(fā)生錯誤返回負值。可以傳入NULL值,表示不關(guān)心任何文件的讀變化。
fd_set* writefds:是指向fd_set結(jié)構(gòu)的指針,這個集合中應(yīng)該包括文件描述符,我們是要監(jiān)視這些文件描述符的寫變化的,即我們關(guān)心是否可以向這些文件中寫入數(shù)據(jù)了,如果這個集合中有一個文件可寫,select就會返回一個大于0的值,表示有文件可寫,如果沒有可寫的文件,則根據(jù)timeout參數(shù)再判斷是否超時,若超出timeout的時間,select返回0,若發(fā)生錯誤返回負值。可以傳入NULL值,表示不關(guān)心任何文件的寫變化。
fd_set * errorfds:同上面兩個參數(shù)的意圖,用來監(jiān)視文件錯誤異常。
struct timeval* timeout:是select的超時時間,這個參數(shù)至關(guān)重要,它可以使select處于三種狀態(tài):
第一,若將NULL以形參傳入,即不傳入時間結(jié)構(gòu),就是將select置于阻塞狀態(tài),一定等到監(jiān)視文件描述符集合中某個文件描述符發(fā)生變化為止;
第二,若將時間值設(shè)為0秒0毫秒,就變成一個純粹的非阻塞函數(shù),不管文件描述符是否有變化,都立刻返回繼續(xù)執(zhí)行,文件無變化返回0,有變化返回一個正值;
第三,timeout的值大于0,這就是等待的超時時間,即select在timeout時間內(nèi)阻塞,超時時間之內(nèi)有事件到來就返回了,否則在超時后不管怎樣一定返回,返回值同上述。
1.3 select 返回值
負值:select錯誤
正值:某些文件可讀寫或出錯
0:等待超時,沒有可讀寫或錯誤的文件
2. 例子
在有了select后可以寫出像樣的網(wǎng)絡(luò)程序來!
舉個簡單的例子,就是從網(wǎng)絡(luò)上接受數(shù)據(jù)寫入一個文件中。
用來循環(huán)讀取鍵盤輸入的例子:
將例子程序作一修改,加上了time out,并且考慮了select得所有的情況: