文件I/O与I/O多路复用

1.文件表

进程打开一个文件,会与三个表发生关联,分别是:文件描述符表、文件表、索引结点表。

在 Linux/Unix 系统中,Socket 确实被抽象为一种特殊的文件。这是 Unix "一切皆文件"(Everything is a File)设计哲学的体现。

在 /proc/pid/fd/ 中可见。

为什么fork之后,要把不用的文件关掉

防止资源泄漏,每个打开的文件描述符都会占用系统资源。系统对单个进程和整个系统能打开的文件描述符数量都有限制。

C 标准库(stdio)中 fread 和 fwrite 的缓冲机制。这是理解高性能 I/O 操作的关键之一。

一、为什么需要缓冲?

核心目的:减少系统调用的次数,极大提升 I/O 效率。

stdin 和 stdout 如果指向终端(交互式设备),通常是行缓冲;如果被重定向到文件,则通常变为全缓冲。

2.多路复用

1.select 的作用

1.select 的核心作用是:允许一个进程(或线程)同时监视多个文件描述符(File Descriptor, FD),等待其中一个或多个 FD 变得"可读"、"可写"或"发生异常",然后通知程序进行相应的读写操作。

简单来说,它解决了"一个服务端程序如何同时高效地处理多个客户端连接"的问题,而不需要为每个连接都创建一个进程或线程。

2.为什么要用 select?------ 解决的核心问题

在 select/poll/epoll 这类技术出现之前,处理多个网络连接的常规方法是:

多进程模型:accept 一个连接就 fork 一个子进程来处理。问题:进程创建、销毁、上下文切换开销巨大,资源占用高,难以应对成千上万的连接(这就是著名的 C10K problem)。

多线程模型:accept 一个连接就创建一个线程。问题:虽然比进程轻量,但大量线程的上下文切换开销依然很大,而且编程中线程同步非常复杂。

非阻塞忙轮询(Busy-polling):将 socket 设为非阻塞,然后在一个循环里依次调用 recv 检查每个连接。问题:CPU 资源被极大浪费,因为大部分时间都是在做无用的检查。

select 的出现就是为了克服上述模型的缺点:

资源高效:一个线程就能管理成百上千个网络连接,大大降低了系统资源消耗。

避免忙等待:在没有事件发生时,select 会阻塞(睡眠),让出 CPU 资源。只有当事件发生(或有信号中断)时,它才会被唤醒,从而高效利用 CPU。

统一事件管理:可以同时等待多种类型的 I/O 事件(可读、可写、异常),使得程序能够统一处理所有连接的 I/O 活动。

3.核心步骤对应代码

设置集合:FD_ZERO, FD_SET 初始化并填充需要监视的 fd(如监听 socket 和所有客户端 socket)。

阻塞等待:调用 select(nfds, &readfds, NULL, NULL, NULL)。

检查结果:select 返回后,使用 FD_ISSET 遍历所有 fd,判断哪些 fd 上有事件发生。

处理事件:

如果是监听 socket 可读,说明有新连接,调用 accept。

如果是客户端 socket 可读,说明有数据到来,调用 recv。

2.poll 相对于select的真正优势

既然都需要轮询,那 poll 的优势在哪里?它解决的是 select 的其他设计缺陷:

问题 select poll

连接数限制 有,受 FD_SETSIZE (通常1024) 限制 无硬性限制,仅受系统内存限制

内核反馈机制 使用"传入-传出"参数,每次调用后原始集合被破坏,需要重置。 使用独立的 events(输入)和revents(输出)字段,无需重置。

处理异常 需要额外的 exceptfds 集合。 可以通过 events 字段直接设置

3.epoll

解决了这个轮询问题,epoll 的工作方式是"事件就绪通知":

你告诉内核要监视哪些 FD(通过 epoll_ctl)。

当事件就绪时,epoll_wait 调用返回,它只给你一个装满就绪事件的数组,这个数组的大小恰好就是就绪 FD 的数量。

你直接处理这个数组即可,时间复杂度是 O(就绪的fd数量),而不是 O(总共监视的fd数量)。

epoll_create1: 创建一个 epoll 实例,返回一个文件描述符(epfd),用于管理所有待监视的 FD。

epoll_ctl: 向 epoll 实例(epfd)添加、修改或删除要监视的 FD 及其感兴趣的事件(EPOLLIN, EPOLLOUT 等)。这个操作只需执行一次,而不是每次循环都传递整个集合。

epoll_wait: 等待事件发生。它只返回那些已经就绪的 FD 信息(一个 events 数组),数量就是就绪的 FD 数。应用程序无需遍历所有监视的 FD,直接处理这些就绪的事件即可。这是 O(1) 的复杂度。

相关推荐
大聪明-PLUS4 小时前
像 Docker 一样创建虚拟网络
linux·嵌入式·arm·smarc
6190083366 小时前
linux离线安装elasticsearch8.19.3
linux
IDOlaoluo7 小时前
OpenSSL 1.0.1e 下载解压和运行方法(小白适用 附安装包)
linux
豆是浪个8 小时前
Linux(Centos 7.6)命令详解:sar
linux·运维·centos
fie88898 小时前
CentOS下Bind服务的安装与故障排查
linux·运维·centos
Xの哲學8 小时前
Linux RCU (Read-Copy-Update) 机制深度分析
linux·网络·算法·架构·边缘计算
東雪蓮☆8 小时前
Linux 网络流量监控 Shell 脚本详解(支持邮件告警)
linux·运维·服务器
小跌—9 小时前
Linux:进程信号理解
linux·c++·算法
東雪蓮☆10 小时前
使用 Shell 脚本监控服务器 IOWait 并发送邮件告警
linux·运维·服务器