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

select()函数的第一个参数是什么,c,linux,sockets,unix,C,Linux,Sockets,Unix,我不太明白select函数中第一个参数的用途。将其描述为所有集合中的最大文件描述符,加上1。为什么+1和为什么选择需要此信息?想法是选择功能可以使用第一个参数来优化读取fd_集的定时 在手册中: man select 它说: nfds是三个集合中编号最高的文件描述符,加上1 因此,select函数仅检查fd_集中小于此值的fd,而不是fd_集中所有可能的fd。此大小在FD_SETSIZE常量中定义。在*Nix系统中,文件描述符只是系统表中的索引,而FD_set结构包含与这些索引对应的位掩码。将

我不太明白
select
函数中第一个参数的用途。将其描述为所有集合中的最大文件描述符,加上1。为什么+1和为什么选择需要此信息?

想法是选择功能可以使用第一个参数来优化读取fd_集的定时

在手册中:

man select
它说:

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


因此,select函数仅检查fd_集中小于此值的fd,而不是fd_集中所有可能的fd。此大小在FD_SETSIZE常量中定义。

在*Nix系统中,文件描述符只是系统表中的索引,而
FD_set
结构包含与这些索引对应的位掩码。将描述符添加到
fd_集合
时,相应的位被启用
select()
需要知道描述符的最高值,这样它就可以循环遍历位,并知道在哪个位停止


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

这是(原始)Berkeley套接字实现的偶然细节。基本上,实现使用文件描述符的数量作为一些临时内部位数组的大小调整变量。由于Unix描述符以零开始,因此最大描述符的大小将比每个描述符一个插槽的任何数组的大小小一个。因此,“最大加一”的要求。这个+1调整本来可以被系统调用本身吸收,但事实并非如此


古代历史,仅此而已。结果是,第一个参数的正确解释与其说与描述符值有关,不如说与描述符值的数量有关(即待测试描述符的最大数量)。请参阅的第6.3节(这是Rich Stevens经典文本的修订和更新版本。如果没有,请获取!)

在大多数UNIX内核ABI中,
select
fd\u set*
参数实际上是
无符号*
无符号长*
指向包含位的字数组。
select
的第一个参数告诉内核这些数组有多大以及应该检查多少位

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


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

可能需要对其进行偏移,因为它考虑了从
1到n的有效文件描述符编号,不是
0到(n-1)
。这毫无意义。
fd_集合
中可以有多个值。如果
select()
“仅检查fd_集中小于此值的fd,而不是fd_集中所有可能的fd”,则会跳过值。@RemyLebeau nfds应该是具有最高值+1的文件描述符。i、 e.如果将文件描述符5、9、90放入fd_集中,则将nfd参数设置为91。您在fd_集中放置的任何文件描述符都小于91-并且您感兴趣的描述符都不会被跳过。我了解什么是
nfd
。但是,请看答案的措辞:“选择功能仅检查fd_集中小于此值的fd,而不是fd_集中所有可能的fd”。这意味着可以跳过添加到fd_集的值。只需修改措辞,使其更清楚地表明,
select()
不会检查所有可能的fd,只检查实际添加到fd_集合中的fd。如果添加值>=nfds参数的描述符,则描述符确实会被跳过,这对于+1是有意义的。谢谢你的回答和链接。如果我理解了Remy Lebeau的答案和你的答案,那么内部位数组是否包含在添加描述符时启用的位元素?在最初的Berkeley实现中,每个系统调用都会重新组装内部位数组。调用之间不保持任何状态。Rich Stevens也报道了血淋淋的细节,那么为什么作者不将
fdu集
作为一个链表呢?