2009年2月16日 星期一

c - select

在網絡程序中,一個進程同時處理多個文件描述符是很常見的情況。select()系統調用可以使進程檢測同時等待的多個I/O設備,當沒有設備準備好時,select()阻塞,其中任一設備準備好時,select()就返回。

select 呼叫及伴隨它所引發的巨集共用 fd_set. fd_set 則是一個位元陣列, 而其中每一個位元代表一個有效的檔案敘述結構. select 呼叫接受一個有效的檔案敘述結構並傳回 fd_set 位元陣列, 而該位元陣列中若有某一個位元為 1, 就表示相對映的檔案敘述結構的檔案發生了輸入, 輸出或有例外事件. 而這些巨集提供了所有處理 fd_set 的功能,而我們必需透過FD_ISSET()去判斷是哪一個fd產生變化

select()的調用形式為:
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout);

maxfd:表示所有的file descripter的最大值還要再加1
readfds:指定了被讀監控的file descripter set
writefds:指定了被寫監控的file descripter set
exceptfds:指定了被例外條件監控的file descripter set
timeout:時間到了之後,無論是否有資料進來,select()必需返回。
回傳值為所有readfds, writefds,exceptfds內的個數

參數timeout起了定時器的作用:到了指定的時間,無論是否有設備準備好,都返回調用。timeval的結構定義如下:

struct timeval{
long tv_sec; //表示幾秒
long tv_usec; //表示幾微妙
}

timeout取不同的值,該調用就表現不同的性質:
1.timeout為0,調用立即返回;
2.timeout為NULL,select()調用就阻塞,直到知道有file descripter就緒;
3.timeout為正整數,就是一般的定時器。

select調用返回時,除了那些已經就緒的描述符外,select將清除readfds、writefds和exceptfds中的所有沒有就緒的描述符。select的返回值有如下情況:
1.正常情況下返回就緒的文件描述符個數;
2.經過了timeout時長後仍無設備準備好,返回值為0;
3.如果select被某個信號中斷,它將返回-1並設置errno為EINTR。
4.如果出錯,返回-1並設置相應的errno。

假設執行如下程序後
fd_set readset;
FD_ZERO(&readset);
FD_SET(5, &readset);
FD_SET(33, &readset);

以下表示1組file set,表示fd=5與fd=32是在這一個file set中

再執行如下程序後:
FD_CLR(5, &readset);
則文件描述符集readset對應於文件描述符6的相應位被置為0,如圖2所示:


通常,操作系統通過巨集FD_SETSIZE來聲明在一個進程中select所能操作的文件描述符的最大數目。

系統提供了4個巨集對描述符集進行操作:
#include <sys/select.h>
#include <sys/time.h>
void FD_SET(int fd, fd_set *fdset); // 把fd這一file descripter加入fd set中
void FD_CLR(int fd, fd_set *fdset); // 把fd由fd set中除去
int FD_ISSET(int fd, fd_set *fdset); // 判斷fd是否在這一個fd set中
void FD_ZERO(fd_set *fdset); // 設定這一個fd set沒有含fd

主要參考資料:
Select()系統調用及文件描述符集fd_set的應用

其它參考資料:
等待來自多個訊號來源的輸入
fd_isset(3) - Linux man page
Socket編程中select()的妙用
Linux網絡編程--9. 服務器模型
特殊的 select 函式

沒有留言: