多路转接技术
在之前学习五种 IO 模型时,我们认识到了 IO 的本质是等待 + 拷贝,而多路转接技术可以让等待的过程重叠,即同时等待多个文件描述符的就绪状态。本文将介绍三种实现多路转接的系统调用接口,实际最常用的是 epoll,一些老旧机器上只兼容 select,而 poll 并不常用。
1. select
select 是系统提供的一个多路转接接口。
- select 系统调用可以让程序同时监视多个文件描述符上的事件是否就绪。
- select 的核心工作就是等待,当监视的多个文件描述符中有一个或多个事件就绪时,select 才会成功返回并将对应文件描述符的就绪事件告知调用者。
1.1 select 系统调用及参数介绍
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数说明:
- nfds:需要监视的文件描述符中,最大的文件描述符值 +1(select 底层使用 for 循环遍历实现,该值是为了界定遍历范围)。
- readfds:输入输出型参数,调用时用户告知内核需要监视哪些文件描述符的读事件是否就绪,返回时内核告知用户哪些文件描述符的读事件已经就绪。
- writefds:输入输出型参数,调用时用户告知内核需要监视哪些文件描述符的写事件是否就绪,返回时内核告知用户哪些文件描述符的写事件已经就绪。
- exceptfds:输入输出型参数,调用时用户告知内核需要监视哪些文件描述符的异常事件是否就绪,返回时内核告知用户哪些文件描述符的异常事件已经就绪。
- timeout:输入输出型参数,调用时由用户设置 select 的等待时间,返回时表示 timeout 的剩余时间。
参数 timeout 的取值:
- NULL/nullptr:select 调用后进行阻塞等待,直到被监视的某个文件描述符上的某个事件就绪。
- 0:select 调用后进行非阻塞等待,无论被监视的文件描述符上的事件是否就绪,select 检测后都会立即返回。
- 特定的时间值:select 调用后在指定的时间内进行阻塞等待,如果被监视的文件描述符上一直没有事件就绪,则在该时间后 select 进行超时返回。
返回值说明:
- 如果函数调用成功,则返回有事件就绪的文件描述符个数。
- 如果 timeout 时间耗尽,则返回 0。
- 如果函数调用失败,则返回 -1,同时错误码会被设置。
select 调用失败时,错误码可能被设置为:EBADF、EINTR、EINVAL、ENOMEM。
(1)fd_set 类型
fd_set 类型可以理解为一个位图,每个比特位位置代表是哪个文件描述符,值代表是否就绪。
调用 select 函数之前就需要用 fd_set 结构定义出对应的文件描述符集,然后将需要监视的文件描述符添加到文件描述符集当中。系统提供了一组专门的接口用于操作:
void FD_CLR(int fd, fd_set *set); // 清除描述词组 set 中相关 fd 的位
;
;
;


