Linux I/O 多路复用机制详解

文章目录

  • [1 文件描述符(File Descriptor)](#1 文件描述符(File Descriptor))
    • [1.1 什么是文件描述符?](#1.1 什么是文件描述符?)
    • [1.2 文件描述符与文件的关系](#1.2 文件描述符与文件的关系)
  • [2 文件描述符集合(File Descriptor Set)](#2 文件描述符集合(File Descriptor Set))
    • [2.1 什么是文件描述符集合?](#2.1 什么是文件描述符集合?)
    • [2.2 `fd_set` 结构体](#2.2 fd_set 结构体)
  • [3 `select()` 函数的工作原理](#3 select() 函数的工作原理)
    • [3.1 `select()` 函数概述](#3.1 select() 函数概述)
    • [3.2 `select()` 的工作步骤](#3.2 select() 的工作步骤)
  • [4 `poll()` 函数的工作原理](#4 poll() 函数的工作原理)
    • [4.1 `poll()` 函数概述](#4.1 poll() 函数概述)
    • [4.2 `pollfd` 结构体](#4.2 pollfd 结构体)
    • [4.3 `poll()` 的工作步骤](#4.3 poll() 的工作步骤)
  • [5 `select()` 和 `poll()` 的底层比较](#5 select()poll() 的底层比较)
  • [6 总结](#6 总结)
  • 参考链接
  • 封面

本文将详细解释文件描述符、文件描述符集合,以及 select()poll() 的底层工作原理,以帮助理解 Linux 系统的 I/O 多路复用机制。

1 文件描述符(File Descriptor)

1.1 什么是文件描述符?

在 Unix 和 Linux 系统中,文件描述符(File Descriptor, FD) 是一个非负整数,用于表示已打开的文件、网络套接字、管道等 I/O 资源。每个进程都有一个文件描述符表,这个表记录了进程当前打开的所有文件。

1.2 文件描述符与文件的关系

当一个进程打开一个文件(或其他 I/O 资源),内核会为这个文件分配一个文件描述符,并将其返回给进程。这个文件描述符可以用来标识和访问该文件。例如:

c 复制代码
int fd = open("example.txt", O_RDWR);

在这段代码中,open() 函数返回的 fd 就是文件描述符,它指向已打开的 example.txt 文件。之后,进程可以使用 fd 来读取或写入文件。

2 文件描述符集合(File Descriptor Set)

2.1 什么是文件描述符集合?

文件描述符集合 是一组文件描述符的集合,通常用于 I/O 多路复用函数 select() 中。它们用来表示一组文件描述符的状态(如可读、可写或有错误)。在 Linux 中,文件描述符集合通常由 fd_set 结构体表示。

2.2 fd_set 结构体

fd_set 是一个位图,每个比特位对应一个文件描述符。如果集合中包含某个文件描述符,则该比特位被设置为 1,否则为 0。以下是常用的操作:

  • FD_ZERO:清空集合(将所有比特位设置为 0)。
  • FD_SET:将一个文件描述符加入集合(将对应比特位设置为 1)。
  • FD_CLR:从集合中移除一个文件描述符(将对应比特位设置为 0)。
  • FD_ISSET:检查某个文件描述符是否在集合中(检查对应比特位是否为 1)。

3 select() 函数的工作原理

3.1 select() 函数概述

select() 是一种 I/O 多路复用技术,用于同时监视多个文件描述符的状态(如可读、可写或有错误),并在其中一个或多个文件描述符的状态发生变化时返回。select() 的函数签名如下:

c 复制代码
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

3.2 select() 的工作步骤

  1. 初始化文件描述符集合

    • 使用 FD_ZERO 清空集合,然后用 FD_SET 将感兴趣的文件描述符加入集合。
  2. 调用 select()

    • 内核将挂起进程,并同时监控 readfdswritefdsexceptfds 中的文件描述符,直到其中一个或多个文件描述符的状态发生变化,或者超时。
  3. 内核检查文件描述符状态

    • 内核会遍历所有文件描述符,检查它们的状态。如果一个文件描述符变得可读、可写或发生异常,内核会设置相应集合中的比特位。
  4. select() 返回并处理结果

    • select() 返回文件描述符集合的状态。应用程序可以使用 FD_ISSET 检查哪些文件描述符已经准备好进行 I/O 操作。

4 poll() 函数的工作原理

4.1 poll() 函数概述

poll() 是另一种 I/O 多路复用技术,与 select() 类似,但它使用不同的方式来表示文件描述符集合。poll() 的函数签名如下:

c 复制代码
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

4.2 pollfd 结构体

pollfd 结构体包含了一个文件描述符及其感兴趣的事件和返回的状态:

c 复制代码
struct pollfd {
    int fd;         /* 监视的文件描述符 */
    short events;   /* 感兴趣的事件 */
    short revents;  /* 返回的事件 */
};

4.3 poll() 的工作步骤

  1. 初始化 pollfd 结构体数组

    • 填充 pollfd 结构体数组,指定每个文件描述符和感兴趣的事件。
  2. 调用 poll()

    • 内核将挂起进程,并监视 fds 数组中所有文件描述符的状态,直到某个文件描述符的状态发生变化,或者超时。
  3. 内核检查文件描述符状态

    • 内核逐个检查 fds 数组中的文件描述符,并更新 revents 字段,表示实际发生的事件。
  4. poll() 返回并处理结果

    • poll() 返回后,应用程序检查 revents 字段,确定哪些文件描述符已经准备好进行 I/O 操作。

5 select()poll() 的底层比较

  • 效率

    • select() 使用位图来表示文件描述符集合,这意味着它最多只能监视 FD_SETSIZE(通常是 1024)个文件描述符。同时,由于每次调用 select() 都需要重新设置位图集合,因此在处理大量文件描述符时效率较低。
    • poll() 使用数组来表示文件描述符集合,理论上可以监视任意数量的文件描述符。而且 poll() 不需要每次重置整个数组,只需更新感兴趣的事件即可,因此效率更高。
  • 灵活性

    • poll() 提供了对更多事件类型的支持(如 POLLPRI,表示高优先级数据),而 select() 只支持基本的可读、可写和异常事件。
  • 扩展性

    • select() 的文件描述符数量受限于 FD_SETSIZE,而 poll() 没有这个限制,因此在需要处理大量文件描述符的场景,poll() 更具扩展性。

6 总结

  • 文件描述符 是进程与文件或其他 I/O 资源交互的句柄,每个进程都有自己的一组文件描述符。
  • 文件描述符集合 是多个文件描述符的集合,通常用于 select() 函数中,以同时监视多个文件描述符的状态。
  • select() 通过使用位图集合来监视多个文件描述符的状态,适合处理小数量的文件描述符。
  • poll() 通过使用数组来监视多个文件描述符的状态,适合处理大量文件描述符,且效率更高。

无论是 select() 还是 poll(),它们的核心作用都是提供一种机制来同时监视多个文件描述符的状态,并在这些文件描述符的状态发生变化时通知应用程序,从而实现高效的 I/O 多路复用。


参考链接

封面

由 DALL-E-3 生成

相关推荐
CoolTiger、38 分钟前
【Vmware16安装教程】
linux·虚拟机·vmware16
学习3人组2 小时前
CentOS 中配置 OpenJDK以及多版本管理
linux·运维·centos
厨 神2 小时前
vmware中的ubuntu系统扩容分区
linux·运维·ubuntu
Karoku0662 小时前
【网站架构部署与优化】web服务与http协议
linux·运维·服务器·数据库·http·架构
geek_Chen012 小时前
虚拟机共享文件夹开启后mnt/hgfs/下无sharefiles? --已解决
linux·运维·服务器
(⊙o⊙)~哦3 小时前
linux 解压缩
linux·运维·服务器
牧小七4 小时前
Linux命令---查看端口是否被占用
linux
redcocal4 小时前
地平线秋招
python·嵌入式硬件·算法·fpga开发·求职招聘
鸡鸭扣5 小时前
虚拟机:3、(待更)WSL2安装Ubuntu系统+实现GPU直通
linux·运维·ubuntu