函数原型:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int maxfdpl + 1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tvptr);
功能:用来监视 指定文件(使用文件描述符表示一个文件)的状态是否可 读(readfds),可写(writefds),异常(exceptfds)
参数:
nfds:是要监视所有文件描述符中最大值 + 1
readfds: 要监视可读文件描述符的集合,可以存放多个文件描述符号,一个位表示一个文件符号。
writefds:要监视可写文件描述符的集合
exceptfds: 要监视发生异常的文件描述符的集合
timeout:超时时间
NULL 表示如果所监视文件描述符中没有任何一个发生变化,无限循环监视
timeval 成员全为0,表示,不管有没有变量,直接返回,不等待。
timeval 成员不全为0,表示最多等待指定长的时间 ,这期间如果有任何一个变化了会返回。
return :
>0 准备就绪的描述符数目。表示状态发生变化的文件描述符数量。并不能表示具体是哪个发生了变化,需要【逐个】判断,使用 FD_ISSET(int fd, fd_set *set) 。
0 超时
-1 出错
注意:
当 select 返回时候,它会集合中没有发生的变化的fd 对应 的位清0,发生变化的保持为1。
所以,每次 select 函数返回 后都要重新添加fd到集合中。
FD系列宏:
FD_ZERO(fd_set *fdset);//解除所有和描述符集的绑定(把集合中所有位设置为 0)
FD_SET(int fd, fd_set *fdset);//将相应的文件描述符集和药监听的描述符绑定 (把集合中下标为fd的位设置为 1 )
FD_ISSET(int fd, fd_set *fdset);//判断在监听的描述符是否可读,可写或者异常,返回值:若fd在文件描述符集中,返回非0值,否则返回0
FD_CLR(int fd, fd_set *fdset);//解除一个指定的文件描述符(把集合中下标为fd的位设置为 0 )
文件描述符号是一个数字。
fd_set 本质是一个 unsigned long 类型
#define __NFDBITS (8 * sizeof(unsigned long))
#define __FD_SETSIZE 1024
#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)
typedef struct {
unsigned long fds_bits [__FDSET_LONGS]; //定义一片连续的内存 ,位有一个编号 0,1,2,3,4…… 1023
} __kernel_fd_set;
typedef __kernel_fd_set fd_set;
内核使用它的每个位表示 一个文件描述符号。
示例:如果要查询文件描述符为 1 和5的文件是否可读:
则
readfds 设置为 : 0010 0010 (二进制数量)
设置方法:
FD_SET(1, &readfds) ;
FD_SET(5, &readfds) ;
超时时间结构:
struct timeval {
__kernel_time_t tv_sec; /* 秒 */
__kernel_suseconds_t tv_usec; /* 微秒 */
};
应用层app的实例:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
/* 程序启动时默认打开三个I/O设备文件:
* 标准输入文件 stdin,标准输出文件stdout,标准错误输出文件stderr,分别得到文件描述符 0, 1, 2
* 在标准输入读取9个字节数据。
* 用select函数实现超时判断!
*/
int main(int argc, char ** argv)
{
int ret; // return val
char buf[1000] = {0};
fd_set rdfds;
printf("sizeof(fd_set):%d\n",sizeof(fd_set)); //查看数据类型的大小。128字节
struct timeval tv; //store timeout
while(1) {
FD_ZERO(&rdfds); //clear rdfds
printf("befor set:%08x\n",rdfds);
FD_SET(0, &rdfds); //add stdin handle into rdfds
FD_SET(2, &rdfds); //for test
printf("after set:%08x\n",rdfds);
tv.tv_sec = 3;
tv.tv_usec = 500;
//如果文件调用出错,如文件描述符号不存在,则会直接返回 ,并不会清原来的rdfds,应用程序应该 判断
ret = select(0 + 1, &rdfds, NULL, NULL, &tv);
printf("return select:%08x\n",rdfds);
if(ret < 0) {
perror("\nselect");
continue;
} else if(ret == 0) {
printf("\ntimeout");
continue;
} else {
printf("\nret=%d", ret);
if(FD_ISSET(0, &rdfds)) {
printf("\nreading");
fread(buf, 9, 1, stdin); // read form stdin
}
// read(0, buf, 9); /* read from stdin */
// fprintf(stdout, "%s\n", buf); /* write to stdout */
write(1, buf, strlen(buf)); //write to stdout
//printf("\nlen:%d\n", strlen(buf));
memset(buf,0,1000);
}
}
return 0;
}