I/O多路复用是一种并发处理的机制,允许一个进程通过一种机制监视多个描述符,从而在有多个I/O操作需要处理时选择其中之一进行服务。select
函数是一种常见的实现 I/O 多路复用的系统调用,它允许一个进程同时监视多个文件描述符的可读性、可写性和异常性。
主要特点和步骤包括:
-
构造文件描述符列表:
- 开发者需要创建一个文件描述符集合,包含所有要监视的文件描述符。
- 这个集合通过数据结构
fd_set
来表示。
-
设置文件描述符状态:
- 在监视之前,需要设置文件描述符的状态,指明关心的事件,如可读、可写、异常等。
- 这通过
FD_SET
、FD_CLR
等宏来实现。
-
调用
select
函数:- 调用
select
函数,传入文件描述符集合,同时设置超时时间(或一直阻塞)。 - 调用一个系统函数,监听该列表中的文件描述符,直到这些描述符中的一个或者多个进行 I/O 操作时,该函数才返回。a. 这个函数是阻塞b. 函数对文件描述符的检测的操作是由内核完成的
- 调用
-
在返回时,它会告诉进程有多少(哪些)描述符要进行I/O操作。
// sizeof(fd_set) = 128 1024
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
- 参数:
- nfds : 委托内核检测的最大文件描述符的值 + 1
- readfds : 要检测的文件描述符的读的集合,委托内核检测哪些文件描述符的读的属性
- 一般检测读操作
- 对应的是对方发送过来的数据,因为读是被动的接收数据,检测的就是读缓冲
区
- 是一个传入传出参数
- writefds : 要检测的文件描述符的写的集合,委托内核检测哪些文件描述符的写的属性
- 委托内核检测写缓冲区是不是还可以写数据(不满的就可以写)
- exceptfds : 检测发生异常的文件描述符的集合
- timeout : 设置的超时时间
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
- NULL : 永久阻塞,直到检测到了文件描述符有变化
- tv_sec = 0 tv_usec = 0, 不阻塞
- tv_sec > 0 tv_usec > 0, 阻塞对应的时间
- 返回值 :
- -1 : 失败
- >0(n) : 检测的集合中有n个文件描述符发生了变化
// 将参数文件描述符fd对应的标志位设置为0
void FD_CLR(int fd, fd_set *set);
// 判断fd对应的标志位是0还是1, 返回值 : fd对应的标志位的值,0,返回0, 1,返回1
int FD_ISSET(int fd, fd_set *set);
// 将参数文件描述符fd 对应的标志位,设置为1
void FD_SET(int fd, fd_set *set);
// fd_set一共有1024 bit, 全部初始化为0
void FD_ZERO(fd_set *set);
select
函数的使用使得一个进程可以同时监视多个文件描述符的状态,而不需要为每个文件描述符创建一个线程或进程。这种方式在网络编程中特别有用,允许处理多个连接而不阻塞整个程序。