面试题:网络编程select模式为什么会有1024文件描述符限制?

旅行   2024-10-15 11:53   广东  

欢迎关注本公众号,专注面试题拆解

分享一套视频课程:《C++实现百万并发服务器》 面试需要项目的可以找我获取
,免费分享。 欢迎V:fb964919126







select模式为什么会有1024文件描述符限制?




对于这道题,我觉得言简意赅一句话回答就好:

Linux内核的宏限制了fd_set最多只支持1024。


我们看下源码:

fd_set 结构

fd_set 结构在 <sys/select.h> 中定义,通常看起来像这样:

#define __FD_SETSIZE        1024#define __NFDBITS   (8 * (int) sizeof (long int))
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 */#ifndef _LINUX_POSIX_TYPES_H#define _LINUX_POSIX_TYPES_H
#include <linux/stddef.h>
#undef __FD_SETSIZE#define __FD_SETSIZE 1024
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;
#include <asm/posix_types.h>
#endif /* _LINUX_POSIX_TYPES_H */


通过源码可以看到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电子书资料赠送

精彩文章合集

专题推荐

【专辑】计算机网络真题拆解
【专辑】大厂最新真题
【专辑】C/C++面试真题拆解

CppPlayer
一个专注面试题拆解的公众号
 最新文章