• select用法&原理详解(源码剖析)(转)


    今天遇到了在select()前后fd_set的变化问题,查了好久终于找到一个有用的帖子了,很赞,很详细!!原文链接如下:

    select用法&原理详解(源码剖析)

    我的问题是:

    如下图示:在select()函数前后分别打印fdsread和fdsreaduse两个fd_set,

    在gjm06-1和gjm06-2之所以一样是因为在打印前我令 fdsreaduse = fdsread; ,然后打印后,执行 selet(maxfd,&fdsreaduse,NULL,NULL,NULL); ,再分别打印两个fd_set。

    我的疑惑是:前后两次fdsread都是一样的,为什么fdsreaduse打印出来就不是一样的了。

    通过查看代码,发现了两次打印中间只有一个select()函数对fdsreaduse进行了操作。于是上网搜索,才明白select模型(见下摘录)。

     【注】

    sockfd = 4

    remotefd = 5

    devfd = 6(devicefd)

    我将我所需要的部分,摘录如下:

    简单理解select模型

    理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。

    (1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。

    (2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)

    (3)若再加入fd=2,fd=1,则set变为0001,0011

    (4)执行select(6,&set,0,0,0)阻塞等待

    (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

    所以,我们可以得到select模型的特点:
    (1) 文件描述符个数有限,一般来说这个数目和系统内存关系很大。select使用位域的方式来传递关心的文件描述符,位域就有最大长度。select使用位域的方式传回就绪的文件描述符,调用者需要循环遍历每一个位判断是否就绪,当文件描述符个数很多,但是空闲的文件描述符大大多于就绪的文件描述符的时候,效率很低。

    (2) 将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个 参数。

    (3) 可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。

    通过以上第(5)条性质,可知:在select()时,只对devfd=6进行了write/read,故remotefd和socket都被清空了。

    至此我的问题已解决。

    -----------------------------------------------------------------分  割  线--------------------------------------------------------------------

    此外,还有一个经典的select原理图,放在这里以便大家理解:

    注:select 原理图,摘自 IBM iSeries 信息中心

     既然到这里了,就看一下select函数的原型吧

    select函数原型:

    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
     
    /*参数列表
    int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。 
      
    fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,
    如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,
    select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。    fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,
    如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,
    select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。    fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。    struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态: 第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止; 第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值; 第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
    */

    头文件:

    select位于: #include <sys/select.h> 

    struct timeval位于: #include <sys/time.h> 

    返回值:

    负值:select错误
    正值:某些文件可读写或出错
    0:等待超时,没有可读写或错误的文件

     参数timeout为结构timeval,用来设置select()的等待时间,其结构定义如下:

    struct timeval
    {
        time_t tv_sec;//second
        time_t tv_usec;//minisecond
    };

    over。。。

    参考:

    本人墙推: Linux中select IO复用机制&使用代码实例

    select处理带外数据&解决socket中多用户问题代码

  • 相关阅读:
    解决Extjs分页工具条Ext.PagingToolbar无法换页问题 子曰
    使用“动软代码生成器”需要注意的问题 子曰
    格式化extjs DateField 的值 子曰
    构造extjs两级联动comBox 子曰
    SQLServer数据库设计表和字段(转) 子曰
    extjs 中取值的方式 子曰
    extjs中的控件无法正常显示 子曰
    extjs表单中的下拉框(comobobox)手动添加空选项 子曰
    C++基础学习笔记
    dhl:弹出div层,可关闭可移动
  • 原文地址:https://www.cnblogs.com/gjmhome/p/14031452.html
Copyright © 2020-2023  润新知