【无标题】I/O复用(epoll)三者区别▲

发布于:2025-05-11 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、SOCKET-IO复用技术

定义:SOCKET - IO复用技术是一种高效处理多个套接字(socket)的手段,能让单个线程同时监听多个文件描述符(如套接字)上的I/O事件(像可读、可写、异常),进而提高程序的并发处理能力,避免为每个套接字创建一个线程或进程带来的资源开销(像一个管家)

是由函数select、poll和 Epoll 支持的

1.select、poll和 Epoll三者的区别:

select:轮回机制,存储容器数组,固定大小

poll:轮回机制,存储容器链表,动态扩展

epoll:事件驱动机制

2. 数据结构与扩展性

机制 存储容器类型 最大连接数限制 动态扩展能力
select 固定大小的位掩码数组 通常为 1024(FD_SETSIZE) ❌ 无法扩展
poll 动态链表(struct pollfd) 无硬性限制(取决于系统资源) ✅ 动态添加
epoll 内核红黑树 + 就绪链表 无硬性限制(仅受内存约束) ✅ 自动管理
  • select:使用固定大小的 fd_set(位掩码)存储文件描述符,需手动管理位操作,扩展性差。
  • poll:使用链表 struct pollfd 存储文件描述符,可动态添加,突破了 select 的限制。
  • epoll:使用内核红黑树高效管理所有待监控的文件描述符,自动扩容。

3. 工作机制

机制 事件触发方式 轮询方式 性能特性
select 水平触发(Level Triggered) 遍历所有文件描述符 O (n) 时间复杂度
poll 水平触发 遍历所有文件描述符 O (n) 时间复杂度
epoll 边缘触发(Edge Triggered)或水平触发 仅遍历就绪链表 O (1) 时间复杂度
  • 水平触发(LT):只要文件描述符就绪(如可读),就会持续通知。
  • 边缘触发(ET):仅在文件描述符状态变化(如从不可读到可读)时通知一次,需立即处理,否则数据可能丢失。

4. 性能对比

场景 select/poll 表现 epoll 表现
连接数少且活跃 效率较高 优势不明显
连接数多但不活跃 性能急剧下降(轮询所有连接) 性能稳定(仅处理就绪连接)
大量并发连接 不适用(受 FD_SETSIZE 限制) 非常高效

二、Epoll函数

基于以上三种方法对比,所以我们选用epoll进行使用较为合适

epoll两种模式的区别:

LT逻辑简单,但效率低,ET反之

核心数据结构是:1个红黑树和1个链表

1.创建

int epoll_create(int size);

参数size表明内核要监听的描述符数量。调用成功时返回一个epoll句柄描述符,失败时返回-1

2.注册要监听的事件类型

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

  • 参数:用于控制 epoll 实例对文件描述符的监听。
    • epfd是由epoll_create函数返回的 epoll 实例的文件描述符。
    • op参数指定操作类型,常见的值有EPOLL_CTL_ADD(将文件描述符fd添加到 epoll 实例的监听列表中)、EPOLL_CTL_MOD(修改文件描述符fd的监听事件)和EPOLL_CTL_DEL(从 epoll 实例的监听列表中删除文件描述符fd)。
    • fd是要进行操作的文件描述符。
    • event是一个指向struct epoll_event结构体的指针,用于指定要监听的事件类型以及关联的数据。
  • 返回值:成功时返回 0;失败时返回 -1,并设置errno以指示错误原因。

3.等待事件的就绪

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

  • 功能:等待 epoll 实例所监听的文件描述符上有事件发生。
    • epfd是 epoll 实例的文件描述符。
    • events是一个struct epoll_event类型的数组,用于存储发生事件的文件描述符及其相关事件信息。
    • maxevents指定了events数组的大小,即最多能返回的事件数量。
    • timeout指定等待的超时时间,以毫秒为单位。如果设置为 -1,则表示无限期等待,直到有事件发生;如果设置为 0,则表示立即返回,不进行等待。
  • 返回值:成功时返回发生事件的文件描述符数量;如果超时则返回 0;失败时返回 -1,并设置errno以指示错误原因。
#include "epollServer.h"

epollServer::epollServer(int port)
{
	this->server = new TCPServer(port);

	init_epoll();
}

void epollServer::init_epoll()
{
    // 创建epoll
    epoll_fd = epoll_create(10);
    if (epoll_fd < 0) {
        perror("epoll_create error");
        return;
    }

    // 添加epoll关注事件
    //struct epoll_event epoll_event;
    epoll_event.data.fd = this->server->getServerfd();
    epoll_event.events = EPOLLIN;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, this->server->getServerfd(), &epoll_event);
    cout << "epoll初始化完成..." << endl;
}

void epollServer::start()
{
    struct epoll_event event_array[10] = { 0 };
    int event_num = 0;
    this->thread_pool = new ThreadPool2(5);
    // 主循环
    while (1) {
        cout << "epoll wait..." << endl;
        event_num = epoll_wait(epoll_fd, event_array, 10, -1);
       // cout<<event_array
        if (event_num < 0) {
            perror("epoll_wait error");
            continue;
        }

        for (int i = 0; i < event_num; i++) {
     
            if (event_array[i].data.fd == this->server->getServerfd()) {//处理连接请求
                cout << "有新客户端连接请求" << endl;
                int client_fd = accept(this->server->getServerfd(), NULL, NULL);
                if (client_fd < 0) {
                    continue;
                }
                // 将新的客户端连接添加到epoll关注列表
                epoll_event.data.fd = client_fd;
                epoll_event.events = EPOLLIN;
                epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &epoll_event);
            }
            else if (event_array[i].events & EPOLLIN) {//处理请求监听的事件
        
               
               
            }
        }
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到