1. select函数

1. 用途

在编程的过程中,经常会遇到许多阻塞的函数,好像read和网络编程时使用的recv, recvfrom函数都是阻塞的函数,当函数不能成功执行的时候,程序就会一直阻塞在这里,无法执行下面的代码。这时就需要用到非阻塞的编程方式,使用select函数就可以实现非阻塞编程。
       select函数是一个轮循函数,循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行。

2. 大致原理

select需要驱动程序的支持,驱动程序实现fops内的poll函数。select通过每个设备文件对应的poll函数提供的信息判断当前是否有资源可用(如可读或写),如果有的话则返回可用资源的文件描述符个数,没有的话则睡眠,等待有资源变为可用时再被唤醒继续执行。详细的原理请看这里

3. 函数定义

该函数声明如下

  1. int select(int nfds, fd_set* readset, fd_set* writeset, fe_set* exceptset, struct timeval* timeout);

  

参数:

nfds           需要检查的文件描述字个数
       readset     用来检查可读性的一组文件描述字。
       writeset     用来检查可写性的一组文件描述字。
       exceptset  用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)
       timeout      超时,填NULL为阻塞,填0为非阻塞,其他为一段超时时间

返回值:

返回fd的总数,错误时返回SOCKET_ERROR

2. fd_set结构体

上面select函数中需要用到两个fd_set形参,这个结构体到底做什么用的呢

fd_set其实这是一个数组的宏定义,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪个句柄可读。

系统提供了FD_SETFD_CLRFD_ISSETFD_ZERO进行操作,声明如下:

  1. FD_SET(int fd, fd_set *fdset); //将fd加入set集合
  2. FD_CLR(int fd, fd_set *fdset); //将fd从set集合中清除
  3. FD_ISSET(int fd, fd_set *fdset); //检测fd是否在set集合中,不在则返回0
  4. FD_ZERO(fd_set *fdset); //将set清零使集合中不含任何fd

  

下面写一段程序探究一下这几个宏的工作:

  1. #include <WINSOCK2.H>
  2. int main()
  3. {
  4. fd_set fdset;
  5. FD_ZERO(&fdset);
  6. FD_SET(1, &fdset);
  7. FD_SET(2, &fdset);
  8. FD_SET(3, &fdset);
  9. FD_SET(7, &fdset);
  10. int isset = FD_ISSET(3, &fdset);
  11. printf("isset = %d\n", isset);
  12. FD_CLR(3, &fdset);
  13. isset = FD_ISSET(3, &fdset);
  14. printf("isset = %d\n", isset);
  15. return 0;
  16. }

  

当使用FD_SET添加完1、2、3、7后,fdset的值如下:

然后经过FD_CLR以后,fd_array[2]就被清除了,数组后面的数据依次往前提,即7被放到了fd_array[2]
       所以isset前后两次打印的值分别为1和0

3. 小结

select的结果会对fd_set造成影响。例如,对于一个监听的socket:

  1. #include <WinSock2.h>
  2. #include <stdio.h>
  3. #pragma comment(lib,"WS2_32.lib")
  4. int main()
  5. {
  6. FD_SET ReadSet;
  7. FD_ZERO(&ReadSet);
  8. WSADATA wsaData;
  9. WSAStartup(MAKEWORD(2, 2), &wsaData); //初始化
  10. SOCKET ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); //定义一个监听套接字
  11. //bind等操作这里省略.... //.....
  12. FD_SET(ListenSocket, &ReadSet); //将套接字加入ReadSet集合中
  13. int isset = FD_ISSET(ListenSocket, &ReadSet); //这里并没有通过select对fd_set进行筛选
  14. printf("Before select, isset = %d\n", isset); //所以这里打印结果为1
  15. struct timeval tTime;
  16. tTime.tv_sec = 10;
  17. tTime.tv_usec = 0;
  18. select(0, &ReadSet, NULL, NULL, &tTime); //通过select筛选处于就绪状态的fd
  19. //这时,刚才的ListenSocket并不在就绪状态(没有连接连入),那么就从ReadSet中去除它
  20. isset = FD_ISSET(ListenSocket, &ReadSet);
  21. printf("After select, isset = %d\n", isset); //所以这里打印的结果为0
  22. system("pause");
  23. return 0;
  24. }

  

       所以可以使用select以及fd的操作来完成异步的网络消息处理,具体的实现请看这里的例子

select函数及fd_set介绍的更多相关文章

  1. (十二)select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET

    select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型:int select(int maxfd,fd_set *rdset ...

  2. select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET

    http://hi.baidu.com/%B1%D5%C4%BF%B3%C9%B7%F0/blog/item/e7284ef16bcec3c70a46e05e.html select函数用于在非阻塞中 ...

  3. select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET(转)

    select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型, 原型: int select(int maxfd,fd_set *rds ...

  4. select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET (转)

    select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型: #include <sys/time.h>       ...

  5. linux网络编程:select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET(转)

    从别人的博客中转载过来了这一篇文章,经过重新编辑排版之后展现于此,做一个知识点保存与学习. select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复 ...

  6. select函数的介绍和使用

    我们所使用的I/O模型一共有五种. 分别为阻塞I/O,非阻塞I/O,I/O复用,信号驱动I/O,异步I/O. 所谓I/O复用就是指管理多个I/O文件描述符,一般会使用(select,poll,epol ...

  7. 一.Select 函数详细介绍【转】

    转自:http://www.cnblogs.com/hjslovewcl/archive/2011/03/16/2314330.html Select在Socket编程中还是比较重要的,可是对于初学S ...

  8. 转 select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET

    select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型:int select(int maxfd,fd_set *rdset ...

  9. Linux驱动 - select函数介绍

    一.select 函数介绍 select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型:          #include & ...

随机推荐

  1. go get 命令

    示例: go get github.com/jinzhu/gorm 下载并安装gorm包. 远程代码库有github,GitLlab,Gogs 命令介绍说明: -fix : 比如,我的代码是一年前1. ...

  2. Java分布式唯一ID生成方案——比UUID效率更高的生成id工具类

    package com.xinyartech.erp.core.util; import java.lang.management.ManagementFactory; import java.net ...

  3. Java中守护线程的总结 thread.setDaemon(true)

    https://www.cnblogs.com/ziq711/p/8228255.html 在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) 用个比较 ...

  4. [K8s 1.9实践]Kubeadm 1.9 HA 高可用 集群 本地离线镜像部署

    k8s介绍 k8s 发展速度很快,目前很多大的公司容器集群都基于该项目,如京东,腾讯,滴滴,瓜子二手车,北森等等. kubernetes1.9版本发布2017年12月15日,每是那三个月一个迭代, W ...

  5. Centos7yum源配置PID锁定问题

    在设置centos7的yum源时,执行 yum clean all 出现PID被锁定的问题: 解决的方法就是: rm -rf /var/run/yum.pid 删除这个文件之后就可以恢复正常.

  6. Dubbo(二):zookeeper 注册中心

    zookeeper 注册中心 Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并 ...

  7. 米尔科技MPSoC开发板评测

    米尔科技推出的MYD-CZU3EG开发板搭载的就是UltraScale+ MPSoC平台器件 — XCZU3EG,它集成了四核Cortex-A53 处理器,双核 Cortex-R5 实时处理单元以及M ...

  8. 服务上的图片直接在浏览器上可以打开,但是在img上报404错误处理方法

    在index.html中添加代码如下 <meta name="referrer" content="no-referrer" /> 如果还是存在问题 ...

  9. .NET异步资料收集

    个人认为应该是.NET关于并行编程 / 异步编程最权威的内容来源(虽然看起来里面的内容离现在已经好几年了,但是大部分内容都不过时) https://devblogs.microsoft.com/pfx ...

  10. WebApplicationContext初始化的两种方式和获取的三种方式

    原博客地址:http://blog.csdn.net/lmb55/article/details/50510547 接下来以ContextLoaderListener为例,分析它到底做了什么? app ...