select()函数的第一个参数究竟是什么?

我不太了解select函数中第一个参数的用途。 维基百科将其描述为所有集合中的最大文件描述符,加上1。 为什么select+1,为什么select需要这些信息?

在* Nix系统中,文件描述符只是索引到系统表中, fd_set结构包含与这些索引对应的位掩码。 当描述符被添加到fd_set ,对应的位被启用。 select()需要知道最高描述符值,所以它可以遍历这些位,并知道哪一个停止。

在Windows上,套接字由内核对象的句柄表示,而不是由索引表示。 fd_set结构包含一个套接字句柄数组和一个数组中的套接字数的计数器。 这样, select()可以循环访问数组,因此select()的第一个参数在Windows上被忽略。

这是(原始的)伯克利套接字实现的一个偶然的细节。 基本上,实现使用文件描述符的数量作为一些临时内部位数组的大小变量。 由于Unix描述符以零开始,因此最大的描述符将小于每个描述符一个语义的每个数组的大小 。 因此,“最大加一”的要求。 这个加1调整本来可以被系统调用吸收,但是不是。

古代历史就是这样。 结果是第一个参数的正确解释与描述符值的关系不如描述符的数量 (即要测试的描述符的最大数目)少。 参见Stevens等人的 6.3节(这是Rich Stevens的经典文本的一个修改和更新的版本,如果你没有它,就得到它!)

理想的是,select函数可以使用第一个参数来优化读取fd_set的时间。

在手册中:

 man select 

它说:

nfds是三个集合中最高编号的文件描述符,加上1。

所以select函数只在fd_set中检查小于fds的fds,而不检查fd_set中所有可能的fds。 这个大小是在FD_SETSIZE常量中定义的。

在大多数UNIX内核ABI中,要selectfd_set *参数实际上是unsigned *unsigned long * ,它们指向包含位的字的数组。 select的第一个参数告诉内核这些数组有多大以及应该检查多少位。

每个字包含16或18或32或36或64位(取决于机器的字数)。 内核将从用户空间读取nfds/wordsize单词,使用最后一个单词(和其他单词的所有位)的nfds%wordsize低位。

POSIX引入了fd_set数据结构和相关函数,可以轻松管理这些位集,这些集也可以移植到使用其他表示形式和内核ABI级别的其他系统。