函数主要功能
调用select函数,可以让进程指示内核等待多个事件中的任意一个发生,并使进程处于阻塞状态,直到select参数指定的事情或者关心的事情发生时候,才唤醒进程,并且函数返回准备就绪的事件个数。
函数原型:
#include<sys/select.h>
int select(int maxfdp1 , fd_set*restrict readfds, fd_set *restrict writefds ,fd_set*restrict exceptfds, struct timeval*restruct tvptr);
select的参数主要告诉内核
1)我们关注的描述符(如socket套接字返回的描述符)
2)对于每个描述符我们所关心的条件(是否想从一个给定的描述符读,是否想写一个给定的描述符,是否关心一个给定描述符的异常条件)
3)最长的等待时间
Select函数返回的时候,内核告诉我们:
1)已经准备好的描述符的总数量
2)对于读、写、或者异常者3个条件中的每一个,有哪一些描述符是准备好的
参数解释:
maxfd1是所有描述符中最大值的描述符+1,这个参数告诉内核我们所关注的最大的描述符,
内核只需要在最大的描述符范围内查找打开的位就可以。
timeval类型的参数tvptr执行select函数等待时间发生的时间长度,tvptr==NULL的时候,表示永远等待,当执行的描述符中有一个已准备好时候等待状态结束。
tvptr->tv_sec==0&&tvptr->tv_usec==0时,select不等待,测试完所以制定的描述符后立即返回,这是轮询系统找到多个描述符而不阻塞select函数的方法
tvptr->tv_sec!=0||tvptr->tv_usec!=0 等待的时间
Readfds,writefds,exceptfds;三个都是指向描述符集的指针,这3个描述符集说明了我们关心的可读、可写或者处于异常条件的描述符集合,没个描述符集合都存储在fd_set数据类型中.
对于fd_set数据类型,我们可以直接将它赋值给相同类型的数据类型,以及通过以下四个函数进行操作
#include<sys/select.h>
Int FD_ISSET(int fd,fd_set* fset)
//判断一个文件描述符是否在集合内,在未调用select函数的时候,调用FD_ISSET时候,如果一个文件描述符在集合fset中,那么返回真,否者返回假,如果已经调用了select函数,select函数返回之后,如果文件描述符fd在集合fset中,并且文件描述符已经准备好可以被读或被写,那么调用FD_ISSET返回的是真,付过文件符fd有准备好,那么即使fd在集合
fset中,FD_ISSET返回的还是假
这里“准备好”指的是进程可以直接对文件描述符进行读写而不处于阻塞状态,例如对于read函数,如果当前可以从网络中读取远程发来的数据,那么read所操作的文件描述符就处于准备好的状态,如果read没有数据读取,处于阻塞状态,那么read所操作的文件描述符就没有准备好
void FD_CLR(int fd,fd_set*fset);//清除fd位
void FD_SET(int fd,fd_set*fset);//将fd添加到集合fd_set中
void FD_ZERO(int fd,fd_set*fset);//清空fd_set
#include<stdio.h>
#include<sys/select.h>
#include<unistd.h>
/*程序目的:
验证select函数监测的文件描述符准备好可以被读取时就立即返回,并且FD_SET()相应的文件描述符返回的是真,如果监测的文件描述符没有
准备好,返回的是假
*/
int main()
{
int max;
fd_set readfds,writefds,temps;
struct timeval tvptr;
FD_ZERO(&readfds);//在使用fd_set变量时一定要进行初始化
FD_SET(STDIN_FILENO,&readfds);
temps=readfds;//fd_set同类型的变量可以直接赋值
FD_ZERO(&writefds);
FD_SET(STDOUT_FILENO,&writefds);
tvptr.tv_sec=3;//将等待时间设置为3.5秒
tvptr.tv_usec=500000;
/*
1)使用select函数监测STDOUT_FILENO,STDIN_FILENO
*/
max=STDOUT_FILENO>STDIN_FILENO?STDOUT_FILENO:STDIN_FILENO;//求最大的文件描述符
/*select使进程处于阻塞状态,知道时间到了或者所关心的实践到来,如STDOUT_FILENO或者STDIN_FILENO
可读,那么select函数就会返回,进程继续执行*/
select(max+1,&readfds,&writefds,NULL,&tvptr);
/*如果STDIN_FILENO或STDOUT_FILENO准备可以被读取,那么就会输出提示*/
if(FD_ISSET(STDIN_FILENO,&readfds))
{
/*由于STDOUT_FILENO对于进程来说始终是可输出的已准备好状态,所以执行的之后select没有等STDIN_FILENO准备好
就直接返回,虽然STDIN_FILENO在readfds中,但是STDIN_FILENO仍是未准备好状态,所以FD_ISSET()返回的是false,
这句永远不会输出*/
printf("STDIN_FILENO is seted\n");
}
if(FD_ISSET(STDOUT_FILENO,&writefds))
{
printf("STDOUT_FILENO is seted\n");
}
/*2)使用select函数只监测STDIN_FILENO*/
/*调用之后,readfds和writefds都会被清空,以下进行验证*/
if(FD_ISSET(STDIN_FILENO,&readfds))
{
printf("STDIN_FILENO is still in readfds\n");
}
else
{
printf("STDIN_FILENO is not in readfds\n");
}
//FD_SET(STDIN_FILENO,&readfds);
/*单独监测STDIN_FILENO,在规定的时间内如果标准输入准备好(按回车)着输出提示*/
FD_ZERO(&writefds);
readfds=temps;//temps以保存readfds的值
max=max>STDIN_FILENO?max:STDIN_FILENO;
select(max+1,&readfds,&writefds,NULL,&tvptr);
if(FD_ISSET(STDIN_FILENO,&readfds))
{
printf("STDIN_FILENO is settd\n");
/*if(FD_ISSET(STDOUT_FILENO,&writefds))
printf("STDOUT_FILENO is seted\n");*/
}
else
{
printf("time over\n");
}
return 0;
}