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編譯通過,可以自行測試運行,本程序測試了兩個描述符,一個標準輸入,一個是文件描述符。