我们先在源码中看看fd_set的定义
typedef long int __fd_mask; // 位掩码数据类型,用于描述一组文件描述符
#undef __NFDBITS // 如果前面有宏定义则取消它
// 定义一个 __fd_mask 变量可以保存多少个文件描述符的状态信息
// 由于我是64位机,所以sizeof得到8,再乘8即得到一个__fd_mask保存64个文件描述符的状态信息
#define __NFDBITS (8 * (int) sizeof (__fd_mask))
#define __FD_ELT(d) ((d) / __NFDBITS)
#define __FD_MASK(d) ((__fd_mask) 1 << ((d) % __NFDBITS))
/* fd_set for select and pselect. */
typedef struct
{
#ifdef __USE_XOPEN
__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
// 两个只是命名的区别
#else
__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif
} fd_set;
__FD_SETSIZE是一个宏定义,为1024,NFDBITS之前求过是64,所以我们的fds_bits数组的个数为16个
我们做个实验:
#include <stdio.h>
#include<sys/select.h>
int main(int argc, char **argv)
{
int j;
unsigned char gap = 0x12; // 该值作为锚点,到时候set越界时gap的最低位被位运算覆盖。
fd_set readfds;
int sd[2500];
unsigned long dist;
printf("gap value is :0x%x\n", gap);
// dist是readfds和附近gap之间的空间大小,即readfds最大的可用空间。每台机器可能不同,不过都在1024左右
dist = (unsigned long)&gap - (unsigned long)&readfds;
FD_ZERO(&readfds);
// dist*8 + 1即让readfds越界1个bit。
// 由于gap为0x12,二进制10010,越界1个bit,可以预期FD_SET会置位0x12的最低位。
// 结果就是0x13
for (j = 0; j < dist*8 + 1; j++) {
sd[j] = j;
FD_SET(sd[j], &readfds);
}
printf("j %d .", j);
printf("after FD_SET. gap value is :0x%01x bytes space:%d\n", gap, dist);
}
结果为
gap value is :0x12
dist is 135
j 1081 .after FD_SET. gap value is :0x13 bytes space:135
这意味着,实际上 FD_SET宏根本不管是否越界以及越界的后果,fd_set也并非严格限制在1024. 但你要是越界使用很可能付出代价。
那我们再看看越界后的fd_set还能正常使用吗?
#include <stdio.h>
#include <netdb.h>
#include<sys/select.h>
#include <sys/socket.h>
#define SIZE 1200
// 全局变量分配在全局数据区而不是栈上,以防被覆盖。
int i = 1001, j;
int sd[SIZE];
struct sockaddr_in serveraddr;
int main(int argc, char **argv)
{
// 使readfds在第一个,覆盖掉我们不再care about的内存.
fd_set readfds;
int childfd;
FD_ZERO(&readfds);
for (j = 0; j < SIZE; j++) {
sd[j] = socket(AF_INET, SOCK_STREAM, 0);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(i++);
bind(sd[j], (struct sockaddr *) &serveraddr, sizeof(serveraddr));
listen(sd[j], 5);
FD_SET(sd[j], &readfds);
}
while (1) {
// select 超过1024的...
if (select(1200, &readfds, 0, 0, 0) < 0) {
perror("ERROR in select");
}
for (j = 0; j < SIZE; j++) {
if (FD_ISSET(sd[j], &readfds)) {
childfd = accept(sd[j], NULL, NULL);
printf("#### %d\n", j);
close(childfd);
}
}
}
}
成功连接,事实说明,文件描述符超过了1024依然OK:
那么如何突破1024的限制呢?
使用malloc动态分配内存咯
fd_set *readfds;
readfds = (fd_set *)malloc(8000/8);
这下readfds就能容纳8000个文件描述符了