如何从select()的fd_set结果中仅循环活动文件描述符?

所以在我目前的服务器实现中,它目前是这样的:

void loop(){ // step 1: clear set fd_set readfds; while(true){ // step 1: FD_ZERO(readfds); // step 2: loop_through_sockets_and_add_active_sockets_to(theset); // step 3: switch(select(FD_SETSIZE, &readfds, 0, 0, &tv)) { case SOCKET_ERROR: patia->receiveEvent(Error, net::getError()); return; case 0: return; } // step 4: loop through sockets and check, using FD_ISSET, which read fd's have incoming data. } } 

现在,不清除fd_set(仅在添加/删除通道时使用FD_SET,FD_CLR)将是更好的方法。

我的问题是,你怎么能在select()之后遍历fd_set,而不检查集合中的每个成员,如果它是集合的一部分,而不使用FD_ISSET?

我的意思是,当你有4000个活动连接时,无论何时有传入的数据,上面的循环都必须经过4000个套接字才能到达正确的连接。 如果所有线程都很活跃,那么复杂性将是n ^ 2!

我的问题是,你怎么能在select()之后遍历fd_set,而不检查集合中的每个成员,如果它是集合的一部分,而不使用FD_ISSET?

你不能。

稍微优化一下, select()返回准备好的描述符的数量,所以如果你保留一个已经处理的数字的计数,当你知道你已经完成所有这些操作而没有到达集合的末尾时,你可以停止。

我的意思是,当你有4000个活动连接时,无论何时有传入的数据,上面的循环都必须经过4000个套接字才能到达正确的连接。 如果所有线程都很活跃,那么复杂性将是n ^ 2!

我没有看到你从哪里得到O(n ^ 2)。 当然,在从select()返回之后,您将在途中处理每个准备好的描述符之后通过该集合。 如果你有4,000个就绪IO描述符,那么在内存C对象中循环遍历4,000个数组的开销将是相当微不足道的。

您很可能已经拥有与每个打开的文件描述符相关联的数据结构,该数据结构是select() ed。 在解复用select()返回的fd_set时,您已经需要一种引用它的方法。

如果文件描述符的数量明显小于FD_SETSIZE ,那么你可能最好迭代它(例如只是打开的文件描述符)并使用FD_ISSET()来检查活动。

您可以。
这将迭代整个集合而不使用ISSET()来自数组的所有FD。 但是你仍然需要触摸fd_set.__fds_bits数组中的所有long

 #include #include int main(void) { fd_set fds; FD_ZERO(&fds); //Fill the set. FD_SET(6, &fds);FD_SET(20, &fds);FD_SET(33, &fds);FD_SET(200, &fds); int i; unsigned long *m = (unsigned long *)__FDS_BITS(&fds); int fd=0; for (i = 0; i < sizeof (fd_set) / sizeof (unsigned long); ++i) //can use int, long or long long. Using long because internal structure is long. { fd=sizeof (unsigned long)*i*8; while(m[i]!=0) { fd+=__builtin_ctzl(m[i]); //Get Number of trailing zero bits in long. printf("FD=%d\n",fd); /*Found FD*/ m[i]>>=(__builtin_ctzl(m[i]))+1; ++fd; } } return 0; } 

这对我使用gcc (SUSE Linux) 4.6.2很好


背景

在我的系统上,fd_set看起来像这样(提取和简化的/usr/include/sys/select.h ):

 typedef struct { __fd_mask __fds_bits[__FD_SETSIZE/__NFDBITS]; } 

__fd_masklong int的typedef。
__FD_SETSIZE/__NFDBITS在我的系统上似乎是16
所以这个数组是(__FD_SETSIZE/__NFDBITS)*sizeof(__fd_mask)*8位。
使用__NFDBITS = 8*sizeof(__fd_mask)您会看到它保存了__FD_SETSIZE位。

查看/usr/include/bits/select.h实际宏的定义,显示__fd_bits用于存储fds。 当fd n置位时, __fd_bitsnth位设置为1。

i386(以及其他 )处理器只有一个操作来计算一个数字的尾随零位。 使用此尾随零的数量,您可以轻松地将__fd_bits条目移动该数量+ 1,您将找到下一个true位。

与循环fd_set的入口集相比,当使用select的返回值进行优化时,需要至少__FD_SETSIZE/__NFDBITS=16循环。

但请确保您的处理器和编译器支持选择循环类型的操作。 当它默认为NOT使用位操作时,但是复杂的实现可能会变得更糟。

如果这实际上比循环更好,则必须certificate已知的fds。