select()和fd_set (設計多sensor的hal時使用)

select()和fd_set

select()函數主要是建立在fd_set類型的基礎上的。fd_set(它比較重要所以先介紹一下)是一組文件描述字(fd)的集合,它用一位來表示一個fd(下面會仔細介紹),對於fd_set類型通過下面四個宏來操作:

 

 

 

 

 

 

 

    fd_set set;

 

 

 

    FD_ZERO(&set);       /* 將set清零使集合中不含任何fd*/

 

 

 

    FD_SET(fd, &set);    /* 將fd加入set集合 */

 

 

 

    FD_CLR(fd, &set);    /* 將fd從set集合中清除 */

 

 

 

    FD_ISSET(fd, &set); /* 測試fd是否在set集合中*/

 

 

 

     

 

 

 

過去,一個fd_set 通常只能包含<32的fd(文件描述字),因爲fd_set其實只用了一個32位矢量來表示fd;現在,UNIX系統通常會在頭文件<sys/select.h>中定義常量FD_SETSIZE,它是數據類型fd_set的描述字數量,其值通常是1024,這樣就能表示<1024的fd。根據fd_set的位矢量實現,我們可以重新理解操作fd_set的四個宏:

 

 

 

 

 

 

 

    fd_set set;

 

 

 

FD_ZERO(&set);      /*將set的所有位置0,如set在內存中佔8位則將set置爲

 

 

 

00000000*/

 

 

 

FD_SET(0, &set);    /* 將set的第0位置1,如set原來是00000000,則現在變爲10000000,這樣fd==1的文件描述字就被加進set中了 */

 

 

 

FD_CLR(4, &set);    /*將set的第4位置0,如set原來是10001000,則現在變爲10000000,這樣fd==4的文件描述字就被從set中清除了 */

 

 

 

 

 

 

 

FD_ISSET(5, &set); /* 測試set的第5位是否爲1,如果set原來是10000100,則返回非零,表明fd==5的文件描述字在set中;否則返回0*/

 

 

 

 

 

 

 

―――――――――――――――――――――――――――――――――――――――

 

 

 

注意fd的最大值必須<FD_SETSIZE。

 

 

 

―――――――――――――――――――――――――――――――――――――――

 

 

 

 

 

 

 

select函數的接口比較簡單:

 

 

 

    int select(int nfds, fd_set *readset, fd_set *writeset,

 

 

 

fd_set* exceptset, struct timeval *timeout);

 

 

 

 

 

 

 

功能:

 

 

 

測試指定的fd可讀?可寫?有異常條件待處理?

 

 

 

    

 

 

 

參數:

 

 

 

nfds    

 

 

 

需要檢查的文件描述字個數(即檢查到fd_set 的第幾位),數值應該比三組fd_set中所含的最大fd值更大,一般設爲三組fd_set中所含的最大fd值加1(如在 readset,writeset,exceptset中所含最大的fd爲5,則nfds=6,因爲fd是從0開始的)。設這個值是爲提高效率,使函數不必檢查fd_set的所有1024位。

 

 

 

readset   

 

 

 

     用來檢查可讀性的一組文件描述字。

 

 

 

writeset

 

 

 

     用來檢查可寫性的一組文件描述字。

 

 

 

exceptset

 

 

 

     用來檢查是否有異常條件出現的文件描述字。(注:錯誤不包括在異常條件之內)

 

 

 

timeout

 

 

 

有三種可能:

 

 

 

1.        timeout=NULL(阻塞:直到有一個fd位被置爲1函數才返回)

 

 

 

2.        timeout所指向的結構設爲非零時間(等待固定時間:有一個fd位被置爲1或者時間耗盡,函數均返回)

 

 

 

3.        timeout所指向的結構,時間設爲0(非阻塞:函數檢查完每個fd後立即返回)

 

 

 

 

 

 

 

返回值:     

 

 

 

返回對應位仍然爲1的fd的總數。

 

 

 

 

 

 

 

Remarks:

 

 

 

三組fd_set均將某些fd位置0,只有那些可讀,可寫以及有異常條件待處理的fd位仍然爲1。

 

 

 

使用select函數的過程一般是:

 

 

 

先調用宏FD_ZERO將指定的fd_set清零,然後調用宏FD_SET將需要測試的fd加入fd_set,接着調用函數select測試fd_set中的所有fd,最後用宏FD_ISSET檢查某個fd在函數select調用後,相應位是否仍然爲1。

 

 

 

 

 

 

 

以下是一個測試單個文件描述字可讀性的例子:

 

 

 

     int isready(int fd)

 

 

 

     {

 

 

 

         int rc;

 

 

 

         fd_set fds;

 

 

 

         struct timeval tv;

 

 

 

   

 

 

 

         FD_ZERO(&fds);

 

 

 

         FD_SET(fd,&fds);

 

 

 

         tv.tv_sec = tv.tv_usec = 0;

 

 

 

   

 

 

 

     rc = select(fd+1, &fds, NULL, NULL, &tv);

 

 

 

         if (rc < 0)   //error

 

 

 

           return -1;

 

 

 

   

 

 

 

         return FD_ISSET(fd,&fds) ? 1 : 0;

 

 

 

     }

 

 

 

     

 

 

 

下面還有一個複雜一些的應用:

 

 

 

//這段代碼將指定測試Socket的描述字的可讀可寫性,因爲Socket使用的也是fd

 

 

 

uint32 SocketWait(TSocket *s,bool rd,bool wr,uint32 timems)    

 

 

 

{

 

 

 

     fd_set rfds,wfds;

 

 

 

#ifdef _WIN32

 

 

 

     TIMEVAL tv;

 

 

 

#else

 

 

 

     struct timeval tv;

 

 

 

#endif   /* _WIN32 */

 

 

 

 

 

 

 

     FD_ZERO(&rfds);

 

 

 

     FD_ZERO(&wfds);

 

 

 

 

 

 

 

     if (rd)     //TRUE

 

 

 

          FD_SET(*s,&rfds);   //添加要測試的描述字

 

 

 

 

 

 

 

     if (wr)     //FALSE

 

 

 

          FD_SET(*s,&wfds);

 

 

 

 

 

 

 

     tv.tv_sec=timems/1000;     //second

 

 

 

     tv.tv_usec=timems%1000;     //ms

 

 

 

 

 

 

 

 

 

 

 

     for (;;) //如果errno==EINTR,反覆測試緩衝區的可讀性

 

 

 

          switch(select((*s)+1,&rfds,&wfds,NULL,

 

 

 

              (timems==TIME_INFINITE?NULL:&tv))) //測試在規定的時間內套接口接收緩衝區中是否有數據可讀

 

 

 

         {                                              //0--超時,-1--出錯

 

 

 

         case 0:     /* time out */

 

 

 

              return 0;

 

 

 

 

 

 

 

         case (-1):    /* socket error */

 

 

 

              if (SocketError()==EINTR)

 

 

 

                   break;

 

 

 

             

 

 

 

              return 0; //有錯但不是EINTR

 

 

 

 

 

 

 

          default:

 

 

 

              if (FD_ISSET(*s,&rfds)) //如果s是fds中的一員返回非0,否則返回0

 

 

 

                   return 1;

 

 

 

              if (FD_ISSET(*s,&wfds))

 

 

 

                   return 2;

 

 

 

              return 0;

 

 

 

         };

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章