文件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) 的复杂度。

相关推荐
AuroBreeze1 小时前
xv6-2023 - primes Lab
linux·运维·服务器
DIY机器人工房1 小时前
NAT 模式、命令行版、桥接模式方式给ubuntu虚拟机配网步骤:
linux·网络协议·ubuntu·嵌入式·桥接模式·diy机器人工房
wdfk_prog2 小时前
[Linux]学习笔记系列 -- lib/sort.c 通用的排序库(Generic Sorting Library) 为内核提供标准的、高效的排序功能
linux·运维·c语言·笔记·stm32·学习·bug
黑马金牌编程3 小时前
简易分析慢 SQL 的流程和方法
linux·数据库·mysql·性能优化·性能分析·慢日志
什么半岛铁盒3 小时前
C++项目:仿muduo库高并发服务器---------LoopThreadPool模块和TcpServer模块的实现
linux·服务器·c++·mysql·ubuntu
Lin_Aries_04214 小时前
容器化 Flask 应用程序
linux·后端·python·docker·容器·flask
鹏大师运维5 小时前
麒麟系统中修改 WPS 默认新建文件格式的方法
linux·操作系统·wps·docx·麒麟·word文档·excel文档
GottdesKrieges6 小时前
OceanBase主备库日志传输服务
linux·oceanbase
stark张宇6 小时前
VMware Workstation 17.5.1 网络故障排查:解决 NAT 模式无法访问外网问题
linux·centos
云动雨颤6 小时前
Linux运维必备:3个内存问题排查命令
linux·运维