linux-select函数详解

写在前面:

主要是参考下边的两篇文章,对文章的内容做了一些记录。

使用背景

select是实现IO多路复用的一种方式。典型场景是网络多并发服务器,服务器需要和多个客户端保持连接,相关源码可参考参考中的第二篇文章。IO多路复用概念参考第三篇文章。

select只是内核提供的IO复用的一种,还有poll、epoll等方式。

函数定义


/* According to POSIX.1-2001 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

函数功能

select()函数允许程序监视多个文件描述符,等待所监视的一个或者多个文件描述符变为“准备好”的状态。所谓的”准备好“状态是指:文件描述符不再是阻塞状态,可以用于某类IO操作了,包括可读,可写,发生异常三种。

使用select来监视文件描述符时,要向内核传递的信息包括:

  • ​ 要监视的文件描述符个数

  • ​ 每个文件描述符,我们可以监视它的一种或多种状态,包括:可读,可写,发生异常三种。

  • ​ 要等待的时间,监视是一个过程,我们希望内核监视多长时间,然后返回给我们监视结果呢?

  • ​ 监视结果包括:准备好了的文件描述符个数,对于读,写,异常,分别是哪儿个文件描述符准备好了。

参数说明

nfds:是一个整数值, 表示集合中所有文件描述符的范围,即所有文件描述符的最大值+1。

意义是待测试的描述集的总个数。 但要注意, 待测试的描述集总是从0, 1, 2, …开始的。 所以, 假如你要检测的描述符为8, 9, 10, 那么系统实际也要监测0, 1, 2, 3, 4, 5, 6, 7, 此时真正待测试的描述符的个数为11个, 也就是max(8, 9, 10) + 1

注意:

  • ​要检测描述符8, 9, 10, 但是你把select的第一个参数定为8, 实际上只检测0到7, 所以select不会感知到8, 9, 10描述符的变化。

  • ​你要检测描述符8, 9, 10, 且你把select的第一个参数定为11, 实际上会检测0-10, 但是, 如果你不把描述如0 set到描述符中, 那么select也不会感知到0描述符的变化。

  • ​ 所以, select感知到描述符变化的必要条件是, 第一个参数要合理, 比如定义为fdmax+1, 且把需要检测的描述符set到描述集中。

fd_set:一个文件描述符集合保存在fd_set变量中,可读,可写,异常这三个描述符集合需要使用三个变量来保存,分别是 readfds,writefds,exceptfds。我们可以认为一个fd_set变量是由很多个二进制构成的数组,每一位表示一个文件描述符是否需要监视。

对于fd_set类型的变量,我们只能使用相关的函数来操作。

void FD_CLR(int fd, fd_set *set);//清除某一个被监视的文件描述符。
int FD_ISSET(int fd, fd_set *set);//测试一个文件描述符是否是集合中的一员
void FD_SET(int fd, fd_set *set);//添加一个文件描述符,将set中的某一位设置成1;
void FD_ZERO(fd_set *set);//清空集合中的文件描述符,将每一位都设置为0;

使用案例:

fd_set readfds;
int fd;
FD_ZERO(&readfds)//新定义的变量要清空一下。相当于初始化。
FD_SET(fd,&readfds);//把文件描述符fd加入到readfds中。
//select 返回
if(FD_ISSET(fd,&readset))//判断是否成功监视
{
    //dosomething
}

readfds:监视文件描述符的一个集合,我们监视其中的文件描述符是不是可读。

writefds:监视文件描述符的一个集合,我们监视其中的文件描述符是不是可写。

exceptfds:用来监视发生错误异常文件

timeout:

struct timeval{
    long tv_sec;//秒
    long tv_usec;//微秒
}

timeout表示select返回之前的时间上限。

如果timeout==NULL,无期限等待下去,这个等待可以被一个信号中断,只有当一个描述符准备好,或者捕获到一个信号时函数才会返回。如果是捕获到信号,select返回-1,并将变量errno设置成EINTR。

如果timeout->tv_sec=0 && timeout->tv_sec=0 ,不等待直接返回,加入的描述符都会被测试,并且返回满足要求的描述符个数,这种方法通过轮询,无阻塞地获得了多个文件描述符状态。

如果timeout->tv_sec!=0 || timeout->tv_sec!=0 ,等待指定的时间。当有描述符复合条件或者超过超时时间的话,函数返回。等待总是会被信号中断。

返回值

成功时:返回三中描述符集合中”准备好了“的文件描述符数量。

超时:返回0

错误:返回-1,并设置 errno

EBADF:集合中包含无效的文件描述符。(文件描述符已经关闭了,或者文件描述符上已经有错误了)。

EINTR:捕获到一个信号。

EINVAL:nfds是负的或者timeout中包含的值无效。

ENOMEM:无法为内部表分配内存。

pselect和select的区别

参考文章1。

代码

参考文章1中的man文档提供的两个实例以及文章2中的多并发服务器实例

参考:

介绍select

  1. https://blog.csdn.net/zujipi8736

  1. https://blog.csdn.net/weixin_49199646

介绍多路复用概念

3.https://blog.csdn.net/Squid87/article/details/123452472

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值