Linux驱动部分我们曾经使用了poll机制完成了在应用层代码读取按键值。这节课介绍的select也很相似。当我们要监控好几个文件描述符的读写呢?如果我们阻塞的去处理其中一个,那第二个怎么办呢?下面我们一起想想办法。
方法一:使用fork将一个进程变成两个进程,每个进程处理一套数据通路,如果使用多个进程,每一个进程就可以阻塞处理read,write函数。但是这也产生了问题:操作什么时候终止?如果子进程接收到了文件结束夫标志,那么该子进程就终止,然后父进程接收到了SIGCHLD信号。但是如果父进程终止,那么应该通知紫禁城停止,为此也需要一个信号。我们可以不使用多进程,而是使用一个进程中的两个线程。这避免了终止进程的复杂性,但是却要求处理线程之间的同步,在减少复杂性方面也是得不偿失。
方法二:配置为不阻塞轮询法,这种思路大部分情况下直接否定了,不做讨论。
方法三:异步IO。基本思想就是告诉内核当一个描述符已经准备好了之后,再用一个信号量通知他。这种技术存在的问题,1、并不是所有的系统都支持(这个我个人没遇到过,接触比较少)2、之中信号对每个进程只有一个SIGOLL或者SIGIO。如果该信号要对两个描述符都起作用,那么接收到此信号时,我们仍旧无法判断是哪一个描述符已经准备好了。
方法四:
I/O多路转换,先构造一张有官描述符的列表,然后调用一个函数,知道这些描述符中的一个准备好的进行I/O时,函数才回去返回,在返回的时候,他会告诉你在那些描述符已经准备好了可以进行I/O。
从 select函数返回后,内核告诉我们一下信息:
•对我们的要求已经做好准备的描述符的个数
•对于三种条件哪些描述符已经做好准备.(读,写,异常)
有了这些返回信息,我们可以调用合适的I/O函数(通常是 read 或 write),并且这些函数不会再阻塞.

#include <sys/select.h>   int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);

返回:做好准备的文件描述符的个数,超时为0,错误为 -1.

首先我们先看一下最后一个参数。它指明我们要等待的时间:
struct timeval{
long tv_sec; /*秒 */
long tv_usec; /*微秒 */
}

有三种情况:
timeout == NULL 等待无限长的时间。等待可以被一个信号中断。当有一个描述符做好准备或者是捕获到一个信号时函数会返回。如果捕获到一个信号, select函数将返回 -1,并将变量 erro设为 EINTR。
timeout->tv_sec == 0 &&timeout->tv_usec == 0不等待,直接返回。加入描述符集的描述符都会被测试,并且返回满足要求的描述符的个数。这种方法通过轮询,无阻塞地获得了多个文件描述符状态。
timeout->tv_sec !=0 ||timeout->tv_usec!= 0 等待指定的时间。当有描述符符合条件或者超过超时时间的话,函数返回。在超时时间即将用完但又没有描述符合条件的话,返回 0。对于第一种情况,等待也会被信号所中断。

中间的三个参数 readset, writset, exceptset,指向描述符集。这些参数指明了我们关心哪些描述符,和需要满足什么条件(可写,可读,异常)。一个文件描述集保存在 fd_set 类型中。fd_set类型变量每一位代表了一个描述符。我们也可以认为它只是一个由很多二进制位构成的数组。如下图所示:

对于 fd_set类型的变量我们所能做的就是声明一个变量,为变量赋一个同种类型变量的值,或者使用以下几个宏来 #include <sys/select.h>
int FD_ZERO(int fd, fd_set *fdset);
int FD_CLR(int fd, fd_set *fdset);
int FD_SET(int fd, fd_set *fd_set);
int FD_ISSET(int fd, fd_set *fdset);ZERO宏将一个 fd_set类型变量的所有位都设为 0,使用FD_SET将变量的某个位置位。清除某个位时可以使用 FD_CLR,我们可以使用 FD_SET来测试某个位是否被置位。
当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零。之后将我们所感兴趣的描述符所对应的位置位,操作如下:

fd_set rset;
int fd;
FD_ZERO(&rset);
FD_SET(fd, &rset);
FD_SET(stdin, &rset);</span>select返回后,用FD_ISSET测试给定位是否置位:if(FD_ISSET(fd, &rset)
{ ... }</span>

具体解释select的参数:
(1)intmaxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错。
说明:对于这个原理的解释可以看上边fd_set的详细解释,fd_set是以位图的形式来存储这些文件描述符。maxfdp也就是定义了位图中有效的位的个数。
(2)fd_setreadfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读;如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
(3)fd_set
writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
(4)fd_seterrorfds同上面两个参数的意图,用来监视文件错误异常文件。
(5)structtimeval
timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
说明:
函数返回:
(1)当监视的相应的文件描述符集中满足条件时,比如说读文件描述符集中有数据到来时,内核(I/O)根据状态修改文件描述符集,并返回一个大于0的数。
(2)当没有满足条件的文件描述符,且设置的timeval监控时间超时时,select函数会返回一个为0的值。
(3)当select返回负值时,发生错误。
理解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)可监控的文件描述符个数取决与sizeof(fd_set)的值。我这边服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。据说可调,另有说虽然可调,但调整上限受于编译内核时的变量值。
(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判断是否有时间发生)。

  • 在 知乎上看到大牛们在讨论一个问题
  • 作者:罗然
    链接:https://www.zhihu.com/question/20114168/answer/31042919
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

先用select接口(poll/epoll,kq,iocp)接受请求,这样可以保证并发,在这个环节他只管收,不处理业务,把FD放到一个buffer(一个q里面),然后业务处理模型对接线程池。可以使复杂业务处理上的负担被分担。select+线程池,这样兼顾了并发(牺牲了一点性能),又保证了因为逻辑代码的简洁性。如果选择完全异步的方式,你就要在业务处理里面使用完全的异步API,至少很多数据库驱动,缓存驱动等等你需要用的到技术都没有提供异步API,很多业务要保障流程的正确是需要同步操作的,而且业务如果全部使用异步API,各种不明确回调和闭包导致内存暴栈的危险上升(我想各位应该被nodejs折磨过吧),对开发人员思考方式和技术实力都有较高的要求。一个部门里面有两个了解epoll就算技术非常NB的核心部门了吧,假若有能正确驾驭epoll,了解各种触发方式,状态机,特别是要能正确读写完整的信息,而没有造成大量的CLOSE_WAIT,是特别特别不易的。我曾在tornado上面搭建过一个线程池。原型参见:nikoloss/iceworld · GitHub虽然不算最完美的解决方案,但是也在工作中省去了很多烦恼。他的效率虽没有原生tornado高,但是非常适合多人合作(尽管如此效率还是要暴webpy几条街)。
对于这个回答我觉得还是非常不错的。

I/O多路转换 select相关推荐

  1. 串口转以太网通信源代码C语言C++编写支持多路转换双向通信支持UDP和TCP客户端

    串口转以太网通信源代码C语言C++编写支持多路转换双向通信支持UDP和TCP客户端 提供,带注释,带设计文档 使用说明介绍 1.功能介绍: 完成了多路网口和串口数据转换的功能. 可实现串口接收到的数据 ...

  2. 串口转以太网通信源代码C语言C++编写支持多路转换双向通信支持UDP和TCP客户端 提供

    串口转以太网通信源代码C语言C++编写支持多路转换双向通信支持UDP和TCP客户端 提供,带注释,带设计文档 使用说明介绍 1.功能介绍: 完成了多路网口和串口数据转换的功能. 可实现串口接收到的数据 ...

  3. I/O多路转换之select与select服务器

    select高效的原因 首先要知道一个概念,一次I/O分两个部分(①等待数据就绪 ②进行I/O),减少等的比重,增加I/O的比重就可以达到高效服务器的目的.select工作原理就是这个,同时监控多个文 ...

  4. C语言网络编程:多路IO select实现多客户端

    文章目录 阻塞式的服务器程序 多线程服务器程序 非阻塞式服务器程序 基于事件响应的服务器程序 事件响应服务器程序的实现`select` 阻塞式的服务器程序 我们接触过最多的最基础的网络通信模型为TCP ...

  5. IO多路转接 ——— select、poll、epoll

    文章目录 I/O多路转接之select select初识 select函数 socket就绪条件 select基本工作流程 select服务器 select的优点 select的缺点 select的适 ...

  6. Linux C语言IO多路转接select函数

    动态数组的实现与前面代码相同,下面是主函数 #include <netdb.h> #include <stdio.h> #include <sys/socket.h> ...

  7. APUE读书笔记-第14章-高级I/O

    14.1 引言 *高级I/O包括非阻塞I/O.记录锁.系统V流机制.I/O多路转换(select和poll函数).readv和writev函数以及存储映射I/O(mmap) 14.2 非阻塞I/O * ...

  8. android 设置activity背景图片,给Activity设置背景颜色

    为了使得错误提示更加显眼,再用Toast+振动效果之外考虑变换整个activity的背景颜色. 尝试一: activity并没像winform一样直接给个属性来设置,就想获取整个activity的la ...

  9. 2021年大数据Hive(五):Hive的内置函数(数学、字符串、日期、条件、转换、行转列)

    全网最详细的Hive文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 系列历史文章 前言 Hive的内置函数 一.数学函数 1. 取整函数: round ...

最新文章

  1. RequireJS示例
  2. Linux文本过滤与处理命令
  3. 关于C语言中单双引号的问题
  4. 伪mac android,Mac,android sdk,monkey压力测试,真机
  5. 最新发布丨游戏市场“超预期”增长背景下,如何加速产品精品化 运营精细化?
  6. json-server模拟后台接口
  7. POWERSPLOIT-Recon(信息侦察)脚本渗透实战
  8. net start mysql服务没有响应控制功能_新服务安装
  9. vSphere 7简介:混合云的功能和技术
  10. wps的ppt如何去掉插入表格的表头与表体之间的空白间隔
  11. ajax jsonp跨域访问,jquery ajax怎么使用jsonp跨域访问
  12. 左耳朵耗子:从“打工人”到技术创业者,我是如何作死的
  13. 【Kubernetes】Pod学习(五)从Pod到容器:Downward API
  14. VirtualBox 安装虚拟机步骤
  15. GraphQL学习第三篇 -在Express中使用GraphQL
  16. 初学者acm的练习题指南
  17. bootstrapvalidat cdn_bootstrapValidator插件下载、官网地址、免费CDN加速地址等
  18. 外卖cps淘宝客如何实现用户与订单的绑定(附外卖cps带分销返利源码)
  19. BGP机房-产品介绍
  20. 连线:iPhone研发不为人知的故事 原型机纰漏百出

热门文章

  1. 434. 字符串中的单词数
  2. leetcode167. 两数之和 II - 输入有序数组(二分查找)
  3. 量子运算 简单通俗例子_什么是量子计算机? 用一个简单的例子解释。
  4. java城市级联一次查询_我的城市没有任何设计活动,所以我自己组织了一次。...
  5. 销售探讨_让我们一起探讨编程资源的领域
  6. 2019-08-01 纪中NOIP模拟赛B组
  7. NSLog打印当前文件,当前函数,当前行数
  8. Docker 部署 postgresql 与 pgadmin4
  9. javascript数组类型
  10. ztree 获取当前选中节点的子节点集合