I/O多路复用之select

本文介绍了Unix下的五种I/O模型之一——select函数,详细解析了select函数的作用、参数含义、模型理解和实现方式,并讨论了其局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  在介绍select之前,我们先来了解Unix下五种I/O模型,分别有:阻塞I/O, 非阻塞I/O, 信号驱动I/O, 异步I/O以及I/O多路复用,而系统提供的select函数就是来实现多路复用I/O模型的,下面将围绕以下几方面来详细介绍一下这个函数:
  1.select函数的作用;
  2.select函数的头文件,原型,各个参数所代表的含义及函数返回值;
  3.select模型的理解;
  4.使用select存在的缺点;
  5.实现一个select_tcp。

1.select函数的作用
通常I/O操作有两个步骤,一个是等,另一个是数据搬迁,select主要是在等的这个状态下阻塞着直到事件发生。select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE *结构的表示就是stdin、stdout、stderr。
2.select函数的头文件,原型及各个参数所代表的含义
这里写图片描述
(1)nfds:是需要监视的最大的文件描述符的值+1。
(2)fd_set:fd_set底层是用位图实现的,每一个位都代表一个文件描述符。
readfds,writefds,exceptfds分别对应于需要检测的可读文件描述符结合,可写描述符集合,异常文件描述符集合,它们都是输入输出型参数。
当作为输入参数时:只要文件描述符集合中对应的位上为1,就表示select需要监视这个描述符的状态。比如readfds里面的文件描述符就代表他们需要等待读事件,writefds里面的文件描述符就代表他们需要等待写事件。
当作为输出参数时,只要文件描述符集合中对应的位上为1,就代表他们等待的事件已经就绪,这是由内核设定的。

下 面的宏提供了处理这三种描述词组的 方式:
这里写图片描述

FD_CLR(int fd,fd_set* set): 用来清除描述词组set中相关fd 的位
FD_ISSET(int fd,fd_set *set): 用来测试描述词组set中相关fd 的位是否为真
FD_SET(int fd,fd_set*set): 用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set): 用来清除描述词组set的全部位参数timeout为

(3)结构timeval, 用来设置select()的等待时间,其结构定义如下:
这里写图片描述

如果参数timeout设为:
NULL:则表 示select()没有timeout,select将 一直被阻塞,直到某个 文件描述符上发 生了事件。
0:表示非阻塞轮询方式,仅检测描述符集合的状态,然后 立即返回,并不等待外部事件的发 生。
特定的时间值:如果在指定的时间段 里没有事件发 生,select将超时返回,返回值为0。

(4)select函数的返回值
调用成功,则返回文件描述符状态已改变的个数;
如果返回0表示在描述符状态改变之前已经超过timeout时间;
如果有错误发生的话,则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。错误值可能为:
a)EBADF 文件描述词为无效的或该文件已关闭
b)EINTR 此调用被信号所中断
c)EINVAL 参数n 为负值。
d)ENOMEM 核心内存不足。

3.select模型的理解
select可监控的文件描述符个数取决于sizeof(fd_set)的值,因为文件描述符是用位图表示的,所以能监控的描述符的最大数量是sizeof(fd_set)*8.
将fd加入select监控集的同时,还要使用一个额外的数组保存select监控集中的fd。
一方面是用于在select返回后,array作为源数据和fd_set进行FD_ISSET判断事件是否就绪;另一方面是select返回之后会把以前加入的但并无事件发生的fd清空,这是由内核清空的,所以每次开始select前都要重新从array中取得fd加入到fd_set中。
还有就是因为select第一个参数是当前要监测的文件描述符的最大值加1,可以在扫描array的同时取得fd的最大值maxfd,用于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被清空。

4.select缺点
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,在fd很多时这个开销很大;
(2)同时每次调用select都需要在内核遍历传进来的所有fd,在fd很多时这个开销也很大;
(3)select支持的文件描述符数量太少了,默认是1024.

5.使用select实现一个简单的网络服务器
代码已上传至github
https://github.com/18309220622/network/commit/07c55ae4bcc66dad2ac79c74fe4f443fb8bb7751select_tcp
在这里通过借助telnet远程登录进行的测试,所以就没有写client.c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值