欢迎关注本公众号,专注面试题拆解
分享一套视频课程:《C++实现百万并发服务器》 面试需要项目的可以找我获取 ,免费分享。 欢迎V:fb964919126
select模式为什么会有1024文件描述符限制?
对于这道题,我觉得言简意赅一句话回答就好:
Linux内核的宏限制了fd_set最多只支持1024。
我们看下源码:
fd_set 结构
fd_set 结构在 <sys/select.h> 中定义,通常看起来像这样:
typedef struct
{
long int fds_bits[__FD_SETSIZE / __NFDBITS];
} fd_set;
这里有个插曲,我最开始去linux源码里面找,从2版本找到6版本也没找到,其实是Linux 内核源码中并没有直接包含名为 sys/select.h 的头文件。相反,select 相关的功能和数据结构定义分散在多个头文件和源文件中。用户空间的 <sys/select.h> 头文件通常是由 glibc 提供的,而不是直接来自内核源码。内核中的 select 实现主要分布在 kernel/select.c 和相关头文件中。
glibc 同时具备标准库和系统库的特性。它实现了 C 标准库和 POSIX 标准库的接口,同时也包含了与操作系统紧密相关的功能。因此,可以说 glibc 是一个标准库,但它也包含了系统库的功能。在实际开发中,glibc 是 Linux 系统中最常用的 C 库之一,几乎所有的 C 程序都会依赖它。
在linux源码里面也定义了__FD_SETSIZE:
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
typedef struct {
unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
} __kernel_fd_set;
/* Type of a signal handler. */
typedef void (*__kernel_sighandler_t)(int);
/* Type of a SYSV IPC key. */
typedef int __kernel_key_t;
typedef int __kernel_mqd_t;
通过源码可以看到fd_set结构体里面就是一个数组,然后数组的大小通过宏定义计算后是16。所以修改了系统的最大文件描述符个数,要想select生效,那就需要重新编译linux内核了。
其实我觉得限制1024更多的是历史原因,select 函数最初是在 1983年的 4.2BSD 系统中引入的,当时的系统资源和硬件能力有限。1024 这个数字可能是基于当时的硬件和内存限制而设定的。
虽然后期硬件发展起来了,但是因为select 的性能随着文件描述符数量的增加而下降,每次调用 select 时,都需要遍历整个位图来检查些文件描述符就绪。对于大量的文件描述符,这种遍历操作会变得非常耗时。因此,一来去增加select的这个限制也没有多大意义,限制文件描述符的数量可以在一定程度上保证性能。二来正好和历史版本保持一致,也不用去考虑兼容性问题。
题外话:看到网上有不少文章研究怎么突破这个1024,大概看了下,通过一些指针偏移之类的操作在一些情况下可以达到,个人认为属实没必要去研究这个。
看下官方对FD_CLR, FD_SET, FD_ISSET, 和 FD_ZERO的注释说明:
The behavior of these macros is undefined if the fd argument is less than 0 or greater than or equal to FD_SETSIZE, or if fd is not a valid file descriptor, or if any of the arguments are expressions with side effects.
翻译一下:
如果 fd 参数小于 0 或者大于等于 FD_SETSIZE,或者 fd 不是一个有效的文件描述符,或者任何参数是具有副作用的表达式,那么这些宏的行为是未定义的。
总结
1:Linux内核宏定义限制了最大就只能是1024
2:限制1024更多的是当时的历史原因,当时硬件还没这么发展迅速
3:文件描述符的数量和select性能不成正比,所以即使后期硬件发展,出于3个方面也没有做修改:
第一:为了和历史老版本兼容保持一致。
第二:增加描述符数量不一定能提高select整体的性能。
第三:已经有了可替代产品poll。
end
CppPlayer
关注,回复【电子书】珍藏CPP电子书资料赠送
精彩文章合集
专题推荐