
1 前言
上一篇文章讲解了五种 IO 模型的基本概念,并通过系统调用使用了非阻塞 IO。 一般的服务器不会使用非阻塞 IO,因为非阻塞 IO 非常耗费 CPU 资源。非阻塞 IO 只有在特定情况下才比较好用。
今天我们来学习多路转接select。
我们知道 IO = 等 + 拷贝。拷贝的前提是底层有数据,没有数据的时候就需要进行等待。为了提高效率可以等待多个文件描述符。多路转接就是等待文件描述符上的新事件,等到就可以通知程序员事件已经就绪,可以进行拷贝!
这个事件可以是:
- 读事件就绪:OS 底层有数据了
- 写事件就绪:OS 底层有空间了
2 认识多路转接 select
我们先来看其作用与定位:
- select 的定位是:只在 IO 中只负责等待,不进行拷贝!并且 select 可以等待多个文件描述符,有新事件就进行通知。
来看 select 系统调用:
SELECT(2) Linux Programmer's Manual SELECT(2) NAME select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing SYNOPSIS #include<sys/select.h>intselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,structtimeval*timeout);voidFD_CLR(int fd, fd_set *set);intFD_ISSET(int fd, fd_set *set);voidFD_SET(int fd, fd_set *set);voidFD_ZERO(fd_set *set);intpselect(int nfds, int readfds, int writefds, int exceptfds,conststructtimespec*timeout,constsigset_t*sigmask); Feature Test Macro Requirements forglibc(see feature_test_macros(7)):pselect(): _POSIX_C_SOURCE >=200112L
select 函数中有 5 个参数,都是用来干什么的呢?
int nfds:输入性参数,表示等待的多个文件描述符最大值加 1。比如等待1 2 5 6 99这几个文件描述符,那么就要传入100。注意不是文件描述符的个数!
struct timeval *timeout:输入输出性参数,这是一个结构体表示微秒级别的时间戳,其中有两个参数分别表示秒和微秒。这个参数告诉 select 在这个时间戳内进行阻塞式 select,超出时间就进行一次返回。如果时间以内等到了新事件,就返回,并把剩余时间返回。传入 {0,0} 就是非阻塞轮询了。传入 nullptr 表示一直阻塞等待事件。

那么现在我们知道了两个参数,我们探索一下返回值:
- 大于 0:有几个就绪了
- 等于 0:超时返回了
- 小于 0:select 出错了
那么其他三个参数呢?首先 fd_set 代表文件描述符集,是用位图进行维护的!位图下标表示文件描述符,该比特位的内容表示对应信息!一共 1024 比特位,可以表示 1024 个文件描述符,下面我们就来了解一下这三个参数:


