I/O多路複用-select()系統調用

select()系統調用可以使進程檢測同時等待的多個I/O設備,當沒有設備準備好時,select()阻塞,其中任一設備準備好時,select()就返回。

   1: #include <sys/select.h>
   2: #include <sys/time.h>
   3:  
   4: int select(int maxfd, 
   5:     fd_set *readfds,     
   6:     fd_set *writefds,
   7:     fe_set *exceptfds,
   8:     const struct timeval *timeout);
   9:  

select的第一個參數是文件描述符集中要被檢測的比特數,這個值必須至少比待檢測的最大文件描述符大1;參數readfds指定了被讀監控的文件描述符集;參數writefds指定了被寫監控的文件描述符集;而參數exceptfds指定了被例外條件監控的文件描述符集。參數timeout起了定時器的作用:到了指定的時間,無論是否有設備準備好,都返回調用。

timeval的結構定義如下:
struct timeval{
    long tv_sec; //表示幾秒
    long tv_usec; //表示幾微妙
}

timeout取不同的值,該調用就表現不同的性質:

1.timeout爲0,調用立即返回;
2.timeout爲NULL,select()調用就阻塞,直到知道有文件描述符就緒;
3.timeout爲正整數,就是一般的定時器。

select調用返回時,除了那些已經就緒的描述符外,select將清除readfds、writefds和exceptfds中的所有沒有就緒的描述符。select的返回值有如下情況:

1.正常情況下返回就緒的文件描述符個數;
2.經過了timeout時長後仍無設備準備好,返回值爲0;
3.如果select被某個信號中斷,它將返回-1並設置errno爲EINTR。
4.如果出錯,返回-1並設置相應的errno。

系統提供了4個宏對描述符集進行操作:

   1: #include <sys/select.h>
   2: #include <sys/time.h>
   3: void FD_SET(int fd, fd_set *fdset);
   4: void FD_CLR(int fd, fd_set *fdset);
   5: void FD_ISSET(int fd, fd_set *fdset);
   6: void FD_ZERO(fd_set *fdset);

宏FD_SET設置文件描述符集fdset中對應於文件描述符fd的位(設置爲1),宏FD_CLR清除文件描述符集 fdset中對應於文件描述符fd的位(設置爲0),宏FD_ZERO清除文件描述符集fdset中的所有位(既把所有位都設置爲0)。使用這3個宏在調用select前設置描述符屏蔽位,在調用select後使用FD_ISSET來檢測文件描述符集fdset中對應於文件描述符fd的位是否被設置。

select的使用方法:

1. 將要監控的文件添加到文件描述符集
2. 調用Select開始監控
3. 判斷文件是否發生變化

如下:

FD_ZERO(&fds); //清空集合
FD_SET(fd1,&fds); //設置描述符
FD_SET(fd2,&fds); //設置描述符
maxfdp=fd1+1; //描述符最大值加1,假設fd1>fd2
switch(select(maxfdp,&fds,NULL,NULL,&timeout))
case -1: exit(-1);break; //select錯誤,退出程序
case 0:break;
default:if(FD_ISSET(fd1,&fds)).... //測試fd1是否可讀

可運行源代碼:

   1: /**
   2: * select()系統調用提供一種實現同步I/O多路複用機制
   3: **/
   4:  
   5: /**
   6: #include <unistd.h>
   7: #include <sys/time.h>
   8: #include <sys/types.h>
   9: 
  10: 
  11: int select (int n,
  12:             fd_set *readfds,
  13:             fd_set *writefds,
  14:             fd_set *exceptfds,
  15:             struct timeval *timeout);
  16:             
  17: FD_CLR(int fd, fd_set *set);
  18: FD_ISSET(int fd, fd_set *set);
  19: FD_SET(int fd, fd_set *set);
  20: FD_ZERO(fd_set *set);
  21: 
  22: **/
  23:  
  24: /** 
  25: timeval
  26: #include <sys/time.h>
  27: struct timeval {
  28: long tv_sec; /* seconds 
  29: long tv_usec; /* microseconds 
  30: };
  31: 
  32: */
  33:  
  34:  
  35: /**
  36: * select()示例程序
  37: **/
  38:  
  39:  
  40: #include <stdio.h>
  41: #include <sys/time.h>
  42: #include <sys/types.h>
  43: #include <unistd.h>
  44: #include <fcntl.h>
  45:  
  46: #define TIMEOUT 5         /* select timeout in seconds */
  47: #define BUF_LEN 1024     /* read buffer in bytes */
  48:  
  49: int max(int a,int b)
  50: {
  51:     return (a>b)?a:b;
  52: }
  53:  
  54: int main (void)
  55: {    
  56:     struct timeval tv;
  57:     fd_set readfds;
  58:     int fd_open, fd ,maxfdp1;
  59:     int ret;
  60:     /* Wait on stdin for input. */
  61:  
  62:     fd_open = open("select_test.c" ,O_RDONLY);
  63:     FD_ZERO(&readfds);
  64:     FD_SET(STDIN_FILENO, &readfds);
  65:     FD_SET(fd_open, &readfds);
  66:     /* Wait up to five seconds. */
  67:     tv.tv_sec = TIMEOUT;
  68:     tv.tv_usec = 0;
  69:     
  70:     /* All right, now block! */
  71:     maxfdp1 = max(STDIN_FILENO  , fd_open) + 1;
  72:     ret = select(maxfdp1,&readfds,NULL,NULL,    &tv);
  73:  
  74:     if(ret == -1){
  75:         perror("select");
  76:         return 1;
  77:     }else if(!ret){
  78:         printf("%d seconds elapsed.\n", TIMEOUT);
  79:         return 0;
  80:     }
  81:     
  82: /*
  83: * Is our file descriptor ready to read?
  84: * (It must be, as it was the only fd that
  85: * we provided and the call returned
  86: * nonzero, but we will humor ourselves.)
  87: */
  88:  
  89:  
  90:     if (FD_ISSET(STDIN_FILENO, &readfds)){
  91:         char buf[BUF_LEN+1];
  92:         int len;
  93:         
  94:         /* guaranteed to not block */
  95:         len = read (STDIN_FILENO, buf, BUF_LEN);
  96:         if (len == -1){
  97:             perror("read");
  98:         //    return 1;
  99:         }
 100:         if (len){
 101:             buf[len] = '\0';
 102:             printf("read: %s\n", buf);
 103:         }
 104:         //return 0;
 105:     }
 106:  
 107:     if (FD_ISSET(fd_open, &readfds)){
 108:         char buf[BUF_LEN+1];
 109:         int len;
 110:         
 111:         /* guaranteed to not block */
 112:         len = read (fd_open, buf, BUF_LEN);
 113:         if (len == -1){
 114:             perror("read");
 115:         //    return 1;
 116:         }
 117:         if (len){
 118:             buf[len] = '\0';
 119:             printf("read: %s\n", buf);
 120:         }
 121:         //    return 0;
 122:     }
 123:  
 124:     
 125:     fprintf (stderr, "This should not happen!\n");
 126:     return 1;
 127: }
 128:  
 129:  

GCC編譯通過,可以自行測試運行,本程序測試了兩個描述符,一個標準輸入,一個是文件描述符。

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