select函数使用浅析 |
您所在的位置:网站首页 › select函数的使用 › select函数使用浅析 |
一、函数原型及参数说明
int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
返回值 : 负值:select错误,正值:某些文件可读写或出错,0:等待超时,没有可读写或错误的文件。
maxfdp :是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
readfds : 是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
writefds :是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
errorfds : 同上面两个参数的意图,用来监视文件是否发生错误异常。
timeout : 是select的超时时间,这个参数至关重要。它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于永久阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来返回正值,超时返回0。
fd_set结构体定义 #define __FD_SETSIZE 1024 typedef __kernel_fd_set fd_set; typedef struct { unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))]; } __kernel_fd_set; 可以看出,fd_set结构体里面是一个无符号长整型的数组,总共有1024/(8 * 4) = 32个元素,然而这并不是说select最多只能监控32个文件的变化。过去,描述符集被作为一个整数位屏蔽码得到实现,但是这种实现对于多于32个的文件描述符将无法工作。描述符集现在通常用整数数组中的位域表示,数组元素的每一位对应一个文件描述符。例如,一个整数占32位,那么整数数组的第一个元素代表文件描述符0到31,数组的第二个元素代表文件描述符32到63,以此类推。宏FD_SET设置整数数组中对应于fd文件描述符的位为1,宏FD_CLR设置整数数组中对应于fd文件描述符的位为0,宏FD_ZERO设置整数数组中的所有位都为0。fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄。 struct timeval结构体定义 struct timeval { __kernel_time_t tv_sec; /* seconds */ __kernel_suseconds_t tv_usec; /* microseconds */ }; tv_sec 单位是秒 tv_usec 单位是微秒,不是毫秒!毫秒的英文单词是millisecond。
二、操作描述字集的四个宏 FD_ZERO(&set); /* 将set清零 */ FD_SET(fd, &set); /* 将fd加入set */ FD_CLR(fd, &set); /* 将fd从set中清除 */ FD_ISSET(fd, &set); /* 如果fd在set中则真 */ 关于FD_ISSET多说一句,select返回时会将没有准备就绪的文件描述符从set中清除,所以FD_ISSET(fd, &set)判断fd是否在set中,如果在说明他没有被清除,该描述符的状态发生了变化(可读、可写或者异常)。
三、一个简单的例子 int main(int argc, char **argv) { int sock; int ret; FILE *fp; struct fd_set fds; struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0。 char buffer[256]={0}; //256字节的接收缓冲区 while(1) { FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化 FD_SET(sock,&fds); //添加描述符 FD_SET(fp,&fds); //添加描述符 maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1 ret = select(maxfdp, &fds, &fds, NULL, &timeout); if(ret < 0) { break; //select错误,退出程序 } else if(ret == 0) { continue; //select超时,再次轮询 } else { if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据 { recvfrom(sock,buffer,256,.....); //接受网络数据 if(FD_ISSET(fp, &fds)) { //测试文件是否可写 fwrite(buffer, 1, sizeof(buffer), fp); //写入文件 } memset(buffer, 0, sizeof(buffer)); } } } return 0; } 四、什么情况下使用select() read()函数和write()函数本身就有阻塞的功能,那么为什么还要用select呢?个人觉得用select主要有以下两个原因 1、select可以监控多个文件描述符的状态,等相应的描述符有变化了再去读写。这时如果不用select的话,每个描述符你都要开一个进程去等待,太浪费资源了。 2、使用select可以进行非阻塞开发。如果不想在网络包还没来之前一直阻塞在recv(),这时候就可以设置select参数timeout的值来处理了。 五、select函数的驱动实现 select为什么会阻塞?我想自己开发一个驱动程序,给上层提供select的功能,我的驱动该怎么做?欲知后事如何,请看我的另一篇博文《select原理及驱动实现》。
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |