当前位置: 首页 > news >正文

Linux学习-TCP并发服务器构建(epoll)

一、核心特点(对比 select、poll 优势)

  1. 存储结构:用红黑树存文件描述符集合,无数量上限,查找高效
  2. 数据拷贝:集合创建在内核层,避免应用层 - 内核层反复拷贝,降低开销
  3. 事件处理:返回到达事件,无需遍历,直接处理,效率高
  4. 触发模式:支持水平触发(低速)和边沿触发(高速),适配不同场景

二、操作流程与关键函数

  1. 创建集合int epoll_create(int size)
    • 功能:通知内核创建文件描述符集合
    • 参数:size 填监测的文件描述符预期数量(实际无严格限制,传正整数即可 )
    • 返回:成功返回集合的文件描述符(用于后续操作),失败返回 -1
  2. 管理集合(增删改)int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
    • 功能:操作 epoll 集合(添加、修改、删除文件描述符及事件)
    • 参数:
      • epfdepoll_create 返回的集合描述符
      • op:操作类型,如 EPOLL_CTL_ADD(添加)、EPOLL_CTL_MOD(修改)、EPOLL_CTL_DEL(删除)
      • fd:要操作的文件描述符
      • event:事件结构体,含 events(事件类型,如 EPOLLIN 读、EPOLLOUT 写 )和 data(关联数据,常用 data.fd 存关注的文件描述符 )
    • 返回:成功返回 0,失败返回 -1
  3. 监测事件int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
    • 功能:通知内核开始监测事件,阻塞等待事件触发
    • 参数:
      • epfd:要监测的集合描述符
      • events:数组,用于存返回的到达事件结果
      • maxevents:最多处理的事件个数(需 ≤ events 数组大小 )
      • timeout:超时时间,-1 表示一直阻塞,0 立即返回,正数为毫秒级超时
    • 返回:成功返回触发的事件数量,失败返回 -1
  4. 事件类型与数据结构
    • epoll_data_t 联合结构体:可存指针、文件描述符等,常用 fd 关联关注的描述符
    • struct epoll_event:含 events(事件类型标记,如 EPOLLIN/EPOLLOUT )和 dataepoll_data_t 类型数据 )

三、核心原理与数据结构

epoll 在内核中维护两个关键数据结构,共同实现高效事件管理:

  1. 红黑树(RB-Tree)

    • 用途:存储所有被监控的 FD 及其关联的事件(如 EPOLLINEPOLLOUT)。
    • 优势:插入、删除、查找 FD 的时间复杂度为 O(log n),支持动态增删大量 FD,无数量上限(仅受系统内存限制)。
  2. 就绪链表(Ready List)

    • 用途:缓存所有触发了事件的 FD(就绪 FD)。
    • 优势:epoll_wait 直接从链表中获取就绪 FD,无需遍历全部 FD,效率极高。

四、关键函数详解

1. 创建 epoll 实例:epoll_create
#include <sys/epoll.h>
int epoll_create(int size);
  • 功能:向内核申请创建一个 epoll 实例(管理 FD 集合的句柄)。

  • 参数size 是历史遗留参数,早期用于提示内核预分配空间,现代 Linux 中已忽略(只需传一个正整数即可)。

  • 返回值:成功返回 epoll 实例的文件描述符(epfd),失败返回 -1(需检查 errno)。

  • 注意epfd 本身也是一个 FD,使用完毕后需用 close(epfd) 释放资源。

2. 管理监控的 FD:epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • 功能:向 epoll 实例添加、修改或删除被监控的 FD 及事件。

  • 参数

    • epfdepoll_create 返回的实例句柄。
    • op:操作类型:
      • EPOLL_CTL_ADD:添加 FD 到监控集合。
      • EPOLL_CTL_MOD:修改已监控 FD 的事件类型。
      • EPOLL_CTL_DEL:从监控集合中删除 FD(此时 event 可设为 NULL)。
    • fd:需要监控的文件描述符(如 socket FD)。
    • event:事件结构体,定义如下:
      struct epoll_event {uint32_t events;  // 事件类型(位掩码)epoll_data_t data;  // 关联数据(用户自定义)
      };typedef union epoll_data {void    *ptr;  // 指针(可关联自定义数据结构)int      fd;   // 常用:关联被监控的 FD 本身uint32_t u32;uint64_t u64;
      } epoll_data_t;
      
    • events 常用类型:
      • EPOLLIN:FD 可读(如 socket 收到数据)。
      • EPOLLOUT:FD 可写(如 socket 发送缓冲区空闲)。
      • EPOLLERR:FD 发生错误(无需主动设置,内核自动触发)。
      • EPOLLET:启用边沿触发模式(默认是水平触发)。
      • EPOLLONESHOT:事件触发一次后自动取消监控(需重新添加才能再次监控)。
  • 返回值:成功返回 0,失败返回 -1(如 FD 已被监控、权限不足等)。

3. 等待就绪事件:epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • 功能:阻塞等待监控集合中就绪的事件,将结果存入 events 数组。

  • 参数

    • epfdepoll 实例句柄。
    • events:用户分配的数组,用于接收就绪事件(输出参数)。
    • maxeventsevents 数组的最大长度(必须 ≥ 1)。
    • timeout:超时时间(毫秒):
      • -1:永久阻塞,直到有事件就绪。
      • 0:立即返回(非阻塞模式)。
      • >0:最多阻塞 timeout 毫秒,超时后返回 0
  • 返回值

    • 成功:返回就绪事件的数量(0 表示超时,无就绪事件)。
    • 失败:返回 -1(如被信号中断,需检查 errno)。

五、与 select/poll 的对比

特性selectpollepoll
监控 FD 数量上限有(通常 1024)无(受系统内存限制)无(受系统内存限制)
事件存储位图(固定大小)数组(动态分配)红黑树 + 就绪链表
事件检测方式遍历所有 FD遍历所有 FD直接取就绪链表(无需遍历)
数据拷贝每次调用拷贝全部 FD每次调用拷贝全部 FD仅初始化时拷贝一次
触发模式仅水平触发仅水平触发水平触发 + 边沿触发
时间复杂度O(n)O(n)O(1)(获取就绪事件)
高并发表现差(FD 越多越慢)较差(遍历开销大)优秀(无遍历开销)

六、典型使用流程

  1. 创建 socket 并绑定端口,设为监听状态(listen)。
  2. 调用 epoll_create 创建 epoll 实例(epfd)。
  3. 调用 epoll_ctl 添加监听 socket 到 epoll 集合(关注 EPOLLIN 事件)。
  4. 循环调用 epoll_wait 等待就绪事件:
    • 若监听 socket 就绪(新连接),调用 accept 获取客户端 socket,添加到 epoll 集合。
    • 若客户端 socket 就绪(可读/可写),处理数据(读/写操作)。
  5. 断开连接时,调用 epoll_ctl 从集合中删除客户端 socket。
http://www.xdnf.cn/news/1381879.html

相关文章:

  • Cesium 入门教程(十一):Camera相机功能展示
  • Burp系列【密码暴力破解+令牌token破解】
  • 深度学习篇---VGGNet网络结构
  • DeepInteraction++基于多模态交互的自动驾驶感知与规划框架
  • 【iOS】Masnory自动布局的简单学习
  • Linux(二) | 文件基本属性与链接扩展
  • Spring Security 深度学习(二): 自定义认证机制与用户管理
  • npm install --global @dcloudio/uni-cli 时安装失败
  • 一天认识一个神经网络之--CNN卷积神经网络
  • QT之双缓冲 (QMutex/QWaitCondition)——读写分离
  • LINUX ---网络编程(三)
  • 如何通过docker进行本地部署?
  • 机器学习回顾(二)——KNN算法
  • Day16_【机器学习概述】
  • 设计模式:组合模式(Composite Pattern)
  • 【数据结构与算法】LeetCode 20.有效的括号
  • Vue 组件循环 简单应用及使用要点
  • 微服务保护和分布式事务-01.雪崩问题-原因分析
  • 步进电机、直流电机常见问题
  • APP手游使用游戏盾SDK为何能有效抵御各类攻击?
  • Java全栈工程师的实战面试:从基础到微服务的全面解析
  • 算法 --- 二分
  • Paimon——官网阅读:非主键表
  • CLIP图像特征提取:`CLIPVisionModel` vs `CLIPModel.get_image_features()`,哪种更适合你的任务?
  • [sys-BlueChi] docs | BluechiCtl命令行工具
  • 滑台模组如何实现电子制造精密加工?
  • Java 大视界 -- 基于 Java 的大数据实时流处理在智能电网分布式电源接入与电力系统稳定性维护中的应用(404)
  • 零基础开发应用:cpolar+Appsmith平民化方案
  • HVV面经总结(二)
  • MySQL事务ACID特性