• select 的使用


    select是在指定时间内(最后一个参数指定的时间)轮询指定fd集合的接口

    1. 需要包含的头文件

    1. #include //select是在指定时间内轮询,所以有时间相关的参数
    2. #include
    3. #include

    2. 函数原型以及参数说明

    1. int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    2. //参数1: int nfds: 最大的文件描述符 + 1
    3. 1)由此可见,select轮询的是int的文件描述符,而非FILE*的
    4. //参数2: 待检测 是否有数据可读取的 fd集合
    5. fd_set实际上是long类型数组,每个数组元素都与一打开的文件句柄建立联系,相关的API如下
    6. (1) FD_ZERO(fd_set*) //将fd_set清空不含任何描述符
    7. 2FD_SET(fd fd_set*) //将fd(int)加入 fd_set集合
    8. 3FD_CLR(fd, fd_set*) //将fd(int)从fd_set中删除
    9. 4FD_ISSET(fd,fd_set*)//select结束后调用,用于判断fd是否在set集合中,
    10. fd在集合中则返回真,不在集合中返回假
    11. //参数3:待检测 是否有数据可写的 fd集合 【目前我所见过的大部分填NULL】
    12. //参数4: 待检测 是否有数据可执行的 fd集合【目前我所见过的大部分填NULL】
    13. //参数5:timeval: 检测的时间

    3. 函数返回值

    1. >0: 就绪描述符的数量
    2. =-1: 出错
    3. 0: 超时(无就绪的描述符)

    使用注意:

    1. 第二到四个参数分别是待检测可读fd集合,  待检测可写fd集合,待检测可执行fd集合,我们可以只填写部分集合,如果三个集合都不填写的话,就得到了一个比sleep更精准的定时器(timeval中精确到微秒,sleep精确到秒)

    2. 第二到四个参数是类似的,只是监视文件的动作不同,以第二个readset为例,会在time参数指定的时间内监视该fd_set集合中是否有可读的文件,如果有,则返回可读的描述符的个数>0,如果在time指定的时间内没有可读的描述符,则返回0. 只有在select发生错误的时候才返回-1 

    3. timeval的三种使用方法:

        1)timeval = NULL: 表示时间无限长,一直到描述符集合中某个描述符变化为止。如果描述符集合中没有发生变化的描述符,则一直阻塞。

        2) timeval = 0:  只轮询描述符集合一次,如果没有发生变化的,返回0; 如果有,则返回变化的描述符的数量。 

        3) timeval > 0: 在该时间内阻塞轮询描述符集合,如果没有发生变化的,则返回0,如果有,则返回发生变化的描述符的数量,

    1. void main()
    2. {
    3. int sock; int fd;
    4. fd_set fds;
    5. struct timeval timeout={0,3}; //select等待3微秒,3微秒轮询,要非阻塞就置0
    6. while(1)
    7. {
    8. FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化
    9. FD_SET(sock,&fds); //添加描述符 sock
    10. FD_SET(fd,&fds); //添加描述符fd
    11. timeout.tv_sec=0;
    12. timeout.tv_usec=3; //select函数会不断修改timeout的值,所以每次循环都应该重新赋值
    13. maxfdp=sock>fd?sock+1:fd+1; //描述符最大值加1
    14. switch(select(maxfdp,&fds,&fds,&fds,&timeout))
    15. {
    16. case -1:
    17. exit (-1);
    18. break; //select错误,直接exit退出
    19. case 0:
    20. break; //0代表超时,没有发生变化的fd
    21. default:
    22. if(FD_ISSET(sock,&fds)) //测试sock是否可读
    23. {
    24. if(FD_ISSET(fd,&fds)) //测试文件是否可写
    25. }
    26. }
    27. }
    28. }

    使用注意:

    1. select 多数情况下是连续调用的,要么是select被包在一个循环里面,要么是软件逻辑保证不停的select 。

    2. select内部会对后面的四个参数进行修改,所以每一次select之前后面的四个参数需要重新设置。

    3. 第二三四个参数(描述符集合)可以是重复的。

    4. select轮询后,会把没有发生变化的描述符从集合中删除,集合中剩余的描述符即发生变化的描述符。所以只需要select后用FD_ISSET检测某个描述符是否还在集合中,即可判断该描述符是否发生了变化。

    扩展:select与 驱动层的关系:

    应用层的selcet调用后,会调用到驱动层的  file_operations->poll  接口,在这个函数里应该调用poll_wait(),将current加到某个等待队列(这里调用poll_wait()),并检查是否有效,如果无效就调用schedule_timeout();去睡眠。事件发生后,schedule_timeout()回来,调用fop->poll(),检查到可以运行,就调用poll_freewait(&table);从而完成select系统调用。重要的是fop->poll()里面要检查是否就绪,如果是,要返回相应标志。

  • 相关阅读:
    nginx的负载均衡包括哪些策略配置?Java如何结合nginx实现负载均衡?
    ES6的方法&类数组转成真正的数组&判断数组的方法
    分布式之计算高性能
    Django--19开发用例系列功能
    【Java面试小短文】HashMap是如何解决Hash冲突的?
    沁恒全方位提供多种USB串口驱动第3代USB转串口产品
    一个结构体 = 另一个结构体(同类型结构体之间可直接赋值操作)
    [手写系列] 带你实现一个简单的Promise
    ThreadX内核源码分析(SMP) - 核间通信(arm)
    LlamaIndex使用指南
  • 原文地址:https://blog.csdn.net/maliangwen_12138/article/details/126692171