select函数使用浅析

您所在的位置:网站首页 select函数的使用 select函数使用浅析

select函数使用浅析

#select函数使用浅析| 来源: 网络整理| 查看: 265

一、函数原型及参数说明     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