典型的使用流程是:
- 创建并初始化文件描述符集(如使用
FD_ZERO
和FD_SET
)。 - 使用
select()
函数,传入这个文件描述符集,select()
会阻塞程序,直到某个文件描述符的状态发生变化。 select()
返回后,可以使用FD_ISSET
宏检查哪个文件描述符已就绪
fd_set wd;
int keyfd = open("/dev/input/event0", O_RDONLY);
FD_ZERO(&wd); // 清空文件描述符集
FD_SET(keyfd, &wd); // 将 keyfd 文件描述符加入集合
// 现在使用 select() 来等待文件描述符的状态变化
int ret = select(keyfd + 1, &wd, NULL, NULL, NULL);
if (ret > 0 && FD_ISSET(keyfd, &wd)) {
// keyfd 文件描述符已准备好读取数据
}
fd_set
用于存储文件描述符的集合。FD_ZERO
清空文件描述符集。FD_SET
将一个文件描述符添加到集合中。- 这些设置用于与
select()
函数一起,监控多个文件描述符的状态。
- 在
select()
函数中,传递给它的第一个参数是要监控的文件描述符集合中最大文件描述符的值加1。这是因为select()
函数使用一个位掩码来跟踪哪些文件描述符需要被监控,它通过检查位掩码中的每一位来确定对应的文件描述符是否准备好进行 I/O 操作.
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds
: 它是select()
要检查的文件描述符集合中最大文件描述符值加1。readfds
: 用于检测哪些文件描述符是否可读。writefds
: 用于检测哪些文件描述符是否可写。exceptfds
: 用于检测异常情况(如错误的文件描述符)。timeout
: 设置等待的超时时间。
为什么 nfds
是最大文件描述符值加1?
select()
内部使用一个位图来表示文件描述符集,每个位对应一个文件描述符。当 select()
遍历位图时,它需要知道需要检查的最大文件描述符是多少,因此它需要传入 nfds
(即最大文件描述符值+1)来确定要检查的范围
select(keyfd + 1, &wd, NULL, NULL, NULL);
这里 keyfd + 1
等于 4,意思是 select()
函数将检查 0
, 1
, 2
, 3
这四个文件描述符(注意,文件描述符从 0 开始编号)。加1是因为 select()
的检查是从 0
到 keyfd
这范围的所有文件描述符。
如果你不加 1
,例如传递 keyfd
而不是 keyfd + 1
,那么 select()
将忽略文件描述符 keyfd
,导致它无法正确监控该文件描述符的状态。