学习记录之select 、poll、epoll

Java 越来约卷,要了解的东西太多了 😂 😂 😂 !!!

一、什么是 select 、poll 、epoll ?

首先,读一个文件,是需要先把文件从硬盘读到内存,再从内存读到程序。这三个都是 IO 多路复用的方案,是一种高效事件通知机制。用于实现 I/O 多路复用的系统调用,他们是 I/O 的记录,不是直接操作文件。它们允许一个进程同时监视多个文件描述符,以确定它们中是否有可读、可写或异常等事件发生。这些系统调用在不同的操作系统中有不同的实现方式。

  1. select:
    • select 是最早出现的多路复用函数,支持所有类型的文件描述符(普通文件、套接字、管道等)。
    • 它使用了一个包含文件描述符集的数据结构,通过参数来告知操作系统监视哪些文件描述符,以及等待的超时时间。
  2. poll:
    • poll 是对 select 的改进,支持更多的文件描述符。
    • 它使用一个 pollfd 数组来表示要监视的文件描述符,同时也可以设置超时时间。
  3. epoll:
    • epoll 是 Linux 特有的多路复用机制,相较于 selectpoll,更为高效,特别适用于处理大量连接。
    • 它采用了事件驱动的方式,只在有事件发生时通知应用程序,而不需要扫描全部的文件描述符。
    • epoll 支持水平触发和边缘触发两种工作方式,边缘触发模式通常更高

二、他们的区别

epoll 和其他的区别

1、select 和 poll 都是被动调用,epoll 是主动通知

2、select和poll都有一个动作,每次都要先把用户态的数据copy到内核,操作完后再copy回用户态。epoll不用,他只用copy一次。

selectpoll 在每次调用时都需要将文件描述符集合从用户态复制到内核态,然后再将事件状态从内核态复制回用户态,这可能会导致额外的开销。而 epoll 利用回调机制,只需要在初始时复制一次,然后通过回调通知应用程序关于事件的状态,减少了不必要的复制,因此更有效率。这也是为什么 epoll 在处理大量并发连接时更加高效的原因之一。

select 和 poll 的区别

要调用 select 方法,需要每次都传递一个文件描述符集合,select 要从集合里循环,找到感兴趣的事件去执行。

poll 就不要,poll 会维护一个文件描述符数组,当 poll 方法被调用完方法自己会更新这个数组,不需要每次都传递。

区别列表

selectpollepoll
操作方式遍历遍历回调
底层实现数组链表哈希表
IO效率每次调用都进行线性遍历,时间复杂度为O(n)每次调用都进行线性遍历,时间复杂度为O(n)事件通知方式,每当fd就绪,系统注册的回调函数就会被调用,将就绪fd放到readyList里面,时间复杂度O(1)
fd拷贝每次调用select,都需要把fd集合从用户态拷贝到内核态每次调用poll,都需要把fd集合从用户态拷贝到内核态调用epoll_ctl时拷贝进内核并保存,之后每次epoll_wait不拷贝
产生年份1984年在BSD中出现1997年才实现2002, 大神 Davide Libenzi 实现

三、文件描述符

文件描述符(File Descriptor)是一种抽象的资源标识符,用于访问文件、套接字、管道、设备和其他 I/O 相关的资源。在许多操作系统中,文件描述符是用来唯一标识打开的文件或其他 I/O 资源的整数。它充当了应用程序和操作系统之间的桥梁,允许应用程序读取、写入和管理这些资源。

文件描述符上的事件是指可以在特定文件描述符上发生的各种 I/O 事件。这些事件包括:

  1. 可读事件:表示文件描述符上有数据可供读取。这通常是输入数据到达的标志。应用程序可以使用读取操作来处理这些数据。
  2. 可写事件:表示文件描述符现在可以接受数据写入。这通常是输出数据的目标。应用程序可以使用写入操作将数据写入文件描述符。
  3. 异常事件:表示在文件描述符上发生了异常。这可能包括套接字连接中断或错误发生等情况。

这些事件通常由操作系统内核检测并报告给应用程序。应用程序可以通过不同的机制(如 selectpollepoll 在 Linux 中)来等待和处理这些事件。这允许应用程序有效地进行异步 I/O 操作,以便同时管理多个文件描述符上的事件,提高应用程序的并发性能。

四、记录一个epoll从硬盘读取文件的方式

要将文件从硬盘通过 epoll 记录的 I/O 事件读入你的程序中,你需要执行以下步骤:

1. 创建 epoll 实例:首先,你需要创建一个 epoll 实例,可以使用 `epoll_create` 函数来完成。

2. 添加文件描述符到 epoll 实例:将你想要监视的文件描述符(通常是一个已打开的文件或套接字)添加到 epoll 实例。使用 `epoll_ctl` 函数,将文件描述符添加到 epoll 的监视列表中,同时指定你感兴趣的事件类型,例如 `EPOLLIN` 表示读事件。你可以添加多个文件描述符到 epoll 实例,以便同时监视多个文件。

3. 进入 epoll 循环:创建一个循环,在这个循环中,你会不断调用 `epoll_wait` 函数,等待文件描述符上发生感兴趣的事件。当有事件发生时,`epoll_wait` 会返回一个包含触发事件的文件描述符列表。

4. 处理事件:一旦 `epoll_wait` 返回触发的事件,你需要处理这些事件。对于读取文件,当文件描述符上发生可读事件时,你可以使用 `read` 或 `readv` 等系统调用来读取数据。在套接字编程中,你可以使用 `recv` 函数。

5. 处理完事件:在处理完事件后,返回到 epoll 循环,等待下一个事件。

这是一个基本的文件读取过程,使用 epoll 来实现非阻塞的 I/O 操作。请注意,epoll 是一种高效的事件通知机制,适用于需要同时监视多个文件描述符的情况,因此适合于高性能的 I/O 操作。

以下是一个简单示例的伪代码

// 创建 epoll 实例
int epoll_fd = epoll_create(1);

// 添加文件描述符到 epoll 实例
struct epoll_event event;
event.events = EPOLLIN; // 监视可读事件
event.data.fd = your_file_descriptor;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, your_file_descriptor, &event);

// 进入 epoll 循环
while (1) {
    struct epoll_event events[MAX_EVENTS];
    int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);

    for (int i = 0; i < num_events; i++) {
        if (events[i].events & EPOLLIN) {
            // 有数据可读
            char buffer[BUFFER_SIZE];
            int bytes_read = read(your_file_descriptor, buffer, BUFFER_SIZE);
            // 处理读取的数据
        }
    }
}

// 关闭 epoll 实例
close(epoll_fd);

这个示例展示了如何使用 epoll 来监视文件描述符上的可读事件,并在有数据可读时执行读操作。在实际应用中,你需要适应你的程序逻辑,包括错误处理和文件关闭等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值