select简介
1.使用场景
写linux程序时,经常存在需要检查好几个输入的状态才能确定下一步行动的情况。例如,像终端仿真器这样的系统,需要有效地同时读取键盘和串行口。如果是在一个单用户系统中,运行一个“忙等待”循环还是可以接受的,它不停地扫描输入设备看是否有数据,如果有数据到达就读取它。
select系统调用就是实现这个功能的接口,它允许程序同时在多个底层文件描述符上等待输入的到达(或输出的完成)。这意味着终端仿真程序可以一直阻塞到有事情可做为止。类似地,服务器也可以通过同时在多个打开的套接字上等待请求到来的方法来处理多个客户。
2.参数说明
#include <sys/types.h>
#include <sys/time.h>
void FD_ZERO(fd_set *fdset); //初始化为空集合
void FD_CLR(int fd, fd_set *fdset); //清除fd
void FD_SET(int fd, fd_set *fdset); //设置fd
void FD_ISSET(int fd, fd_set *fdset); //判断fdset中是否存在fd
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
参数nfds:需要测试的文件描述符数目
参数timeout:指定阻塞的超时时间,如果这个参数是一个空指针,并且套接字上也没有任何活动,这个调用将一直阻塞下去。
select调用用于测试文件描述符集合中,是否有一个文件描述符已处于可读状态或可写状态或错误状态,它将阻塞直到等待某个文件描述符进入上述这些状态。
3.应用实例
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
char buffer[128];
int result, nread;
fd_set inputs, testfds;
struct timeval timeout;
FD_ZERO(&inputs);
FD_SET(0,&inputs);
/* Wait for input on stdin for a maximum of 2.5 seconds. */
while(1) {
testfds = inputs;
timeout.tv_sec = 2;
timeout.tv_usec = 500000;
result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, &timeout);
/* After this time, we test result. If there has been no input, the program loops again.
If there has been an error, the program exits. */
switch(result) {
case 0:
printf("timeout\n");
break;
case -1:
perror("select");
exit(1);
/* If, during the wait, we have some action on the file descriptor,
we read the input on stdin and echo it whenever an <end of line> character is received,
until that input is Ctrl-D. */
default:
if(FD_ISSET(0,&testfds)) {
ioctl(0,FIONREAD,&nread);
if(nread == 0) {
printf("keyboard done\n");
exit(0);
}
nread = read(0,buffer,nread);
buffer[nread] = 0;
printf("read %d from keyboard: %s", nread, buffer);
}
break;
}
}
}
这个程序用select调用来检查标准输入的状态,程序通过实现安排的超时时间每个2.5秒打印一个timeout信息,这是通过select调用返回0来判断的,在文件结尾,标准输入描述被标记为可读,但没有字符可以读取。
上述例子,在select的文件描述符集合中只添加了一个文件描述符,如果有多个文件描述符添加进文件描述符集合,只要其中任何一个文件描述符有活动发生,就可以用FD_ISSET来遍历所有被监听的文件描述符,来检查哪个文件描述符上面有活动发生,这种方法会让进程进入阻塞状态,让CPU一直处于“忙等待”的状态,非常消耗CPU,为了解决这个问题,后面也发展出了更高级的方法。