我不太了解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中,要select
的fd_set *
参数实际上是unsigned *
或unsigned long *
,它们指向包含位的字的数组。 select
的第一个参数告诉内核这些数组有多大以及应该检查多少位。
每个字包含16或18或32或36或64位(取决于机器的字数)。 内核将从用户空间读取nfds/wordsize
单词,使用最后一个单词(和其他单词的所有位)的nfds%wordsize
低位。
POSIX引入了fd_set
数据结构和相关函数,可以轻松管理这些位集,这些集也可以移植到使用其他表示形式和内核ABI级别的其他系统。