发布时间:2026/8/16 5:11:18
Epoll模型 Epoll模型一、底层核心原理每一个epoll模型内部都维护了一棵红黑树当调用epoll_ctl并执行EPOLL_CTL_ADD操作时会将新增的文件描述符fd及关联数据构造成一个新结点存入这棵红黑树中。此外epoll模型中还维护了一个就绪队列当文件描述符上有事件就绪时底层会触发中断操作系统会将该文件描述符对应的红黑树结点放入就绪队列中。当执行epoll_wait时就能直接拿到所有已经就绪的事件对应的fd由程序员自行处理。二、为什么用红黑树存储 fd 数据因为epoll模型可监测的 fd 数量没有硬上限当存在大量 fd 时使用红黑树既可以对 fd 进行高效管理也能实现快速查找与增删操作。三、事件结点如何加入就绪队列红黑树的每一个结点中存储的不只有 fd还包含其他指针字段。当某个结点有事件就绪时不需要重新拷贝整个结点再添加到就绪队列只需要通过结点内的就绪队列指针将结点链入就绪队列即可头插或尾插方式全程无需拷贝结点数据。四、epoll IO 多路转接的整体使用逻辑epoll 是 IO 多路转接模型的一种实现相比 select/poll 效率更高。使用 epoll 的大致流程为调用epoll_create创建一个 epoll 模型获取该模型的句柄本质也是一个文件描述符调用epoll_ctl将需要监听的文件描述符添加到 epoll 模型中进行事件检测在事件循环中调用epoll_wait接口等待就绪的文件描述符五、核心接口详细介绍1.epoll_create功能创建一个 epoll 模型实例返回该实例的句柄文件描述符。说明该函数的参数在新版本内核中已被弃用传入一个大于 0 的数字即可。函数原型#include sys/epoll.h int epoll_create(int size);使用样例// 1.创建epoll模型 _epfd epoll_create(128); if(_epfd 0) { std::cout epoll create error, strerror(errno) std::endl; exit(-1); }2.epoll_ctl功能通过 epoll 句柄对被监听的文件描述符执行增、删、改操作。成功返回 0失败返回 - 1。函数原型#include sys/epoll.h int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);参数说明epfdepoll 模型的句柄op操作类型EPOLL_CTL_ADD将 fd 添加到 epoll 模型中进行监听EPOLL_CTL_MOD修改 fd 对应的监听事件EPOLL_CTL_DEL将 fd 从 epoll 模型中移除fd目标文件描述符event需要监听的事件结构体其中epoll_event结构体定义如下events字段用于设置需要监听的事件常用的有EPOLLIN可读事件、EPOLLOUT可写事件data字段用户自定义数据一般将 fd 存入该字段的fd成员中struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; }epoll_data_t;使用样例// 2.将listen套接字加入到epoll模型中 // 先构建一个事件结构体 struct epoll_event ev; ev.events | EPOLLIN; ev.data.fd _socket-GetSockfd(); int n epoll_ctl(_epfd, EPOLL_CTL_ADD, _socket-GetSockfd(), ev); // 成功返回0 if(n ! 0) { std::cout epoll control error, strerror(errno) std::endl; exit(-1); }3.epoll_wait功能检查 epoll 模型中是否有文件描述符的事件就绪。如果有就绪事件会将这些就绪的 epoll 事件放到events缓冲区中成功时返回就绪事件的个数超时返回 0出错返回 - 1。函数原型#include sys/epoll.h int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);参数说明epfdepoll 模型的句柄events存放就绪事件的输出缓冲区maxevents缓冲区最多能容纳的事件个数timeout超时时间单位是毫秒使用样例// 定义常量表示就绪事件缓冲区的最大容量 const static int gnum 64; // 定义存放就绪事件的缓冲区 struct epoll_event _events[gnum]; // 调用epoll_wait进行等待超过1秒就会返回继续向下执行 int n epoll_wait(_epfd, _events, gnum, 1000); if(n 0) { // 没有事件就绪超时返回 std::cout time out... std::endl; } else if(n 0) { // 有n个事件就绪开始处理事件 HandlerEvents(n); } else { if(errno EINTR) continue; std::cout epoll wait fail, strerror(errno) std::endl; exit(-1); }代码样例——EpollServerDemoEpollServer.hpp#pragma once #include iostream #include memory #include TcpSocket.hpp #include unordered_map #include unistd.h #include sys/epoll.h class EpollServer { private: const static int Nsizeof(fd_set)*8; const static int defaultfd-1; const static int gnum64; public: EpollServer(uint16_t port) :_port(port) ,_socket(std::make_uniqueTcpSocket(port)) ,_epfd(defaultfd) { // 1.创建epoll模型 _epfdepoll_create(128); if(_epfd0) { std::coutepoll create error, strerror(errno)std::endl; exit(-1); } // 2.将listen套接字加入到epoll模型中 // 先构建一个事件结构体 struct epoll_event ev; ev.events|EPOLLIN; ev.data.fd_socket-GetSockfd(); int n epoll_ctl(_epfd, EPOLL_CTL_ADD, _socket-GetSockfd(), ev); // 成功返回0 if(n!0) { std::coutepoll control error, strerror(errno)std::endl; exit(-1); } } void HandlerEvents(int num) { std::cout有Numnum个事件就绪了std::endl; } void Loop() { while(true) { int nepoll_wait(_epfd, _events, gnum, 1000); if(n0) { // 事件没有就绪 std::couttime out...std::endl; } else if(n0) { // 有n个事件就绪了 HandlerEvents(n); } else { if(errnoEINTR) continue; std::coutepoll wait fail, strerror(errno)std::endl; exit(-1); } } } private: int _epfd; uint16_t _port; std::unique_ptrTcpSocket _socket; struct epoll_event _events[gnum]; };TcpSocket.hpp#pragma once #include iostream #include sys/socket.h #include arpa/inet.h #include netinet/in.h #include cstring class InetAddr { public: InetAddr() {} InetAddr(const std::stringip, uint16_t port) :_ip(ip) ,_port(port) { memset(_sockaddr, 0, sizeof(_sockaddr)); _sockaddr.sin_familyAF_INET; _sockaddr.sin_porthtons(port); _sockaddr.sin_addr.s_addrinet_addr(_ip.c_str()); } std::string Ip() { return _ip; } uint16_t Port() { return _port; } // 将addr里面的数据拷贝给自己 void GetInetAddr(sockaddr_in*addr) { memcpy(_sockaddr, addr, sizeof(*addr)); _portntohs(addr-sin_port); _ipinet_ntoa(addr-sin_addr); } private: struct sockaddr_in _sockaddr; std::string _ip; uint16_t _port; }; class TcpSocket { public: TcpSocket(uint16_t port) :_port(port) { _listensocketsocket(AF_INET, SOCK_STREAM, 0); if(_listensocket0) { std::cout监听套接字失败std::endl; exit(-1); } // 设置地址复用 int opt1; setsockopt(_listensocket, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt)); // bind struct sockaddr_in local; memset(local, 0, sizeof(local)); local.sin_familyAF_INET; local.sin_porthtons(port); local.sin_addr.s_addrINADDR_ANY; int nbind(_listensocket, (struct sockaddr*)local, sizeof(local)); if(n0) { std::cout绑定失败std::endl; exit(-1); } // 开启监听 n::listen(_listensocket, _defaultBacklog); if(n0) { std::coutTCP监听失败std::endl; exit(-1); } } int Accept(InetAddr*addr) { struct sockaddr_in peer; socklen_t lensizeof(peer); int sockfdaccept(_listensocket, (struct sockaddr*)peer, len); addr-GetInetAddr(peer); return sockfd; } int GetSockfd() { return _listensocket; } private: uint16_t _port; int _listensocket; // 类内常量所有类对象共享一份 const static int _defaultBacklog16; };Main.cc#include iostream #include EpollServer.hpp int main() { std::unique_ptrEpollServer serverstd::make_uniqueEpollServer(8080); server-Loop(); return 0; }makefiletest_server:Main.cc g -g -o $ $^ -stdc14 .PHONY:clean clean: rm -f test_server

相关新闻

2026/8/16 5:11:18

Ansible Playbook使用案例

Ansible Loop 循环Ansible 的 loop 循环功能允许您对一组数据项重复执行同一任务,从而简化批量操作。本节将通过创建用户账户的实例,演示 loop 的基本用法及其进阶应用。1. 1 基础循环:创建用户以下 Playbook 使用 loop 在目标主机组 db 上创…

2026/8/16 5:11:18

从OpenClaw到Hermes Agent:AI Agent开发框架的开发者体验演进

1. 项目概述:一场开发者工具的“静默迁徙”最近在AI Agent开发圈里,我观察到一个挺有意思的现象:身边不少老伙计,包括一些之前对OpenClaw推崇备至的朋友,都开始默默地转向了另一个工具——Hermes Agent。这不像是一场轰…

2026/8/16 5:11:18

LeetCode刷题的本质:从应试技巧到工程能力的深度转化策略

1. 这篇文章真正要解决的问题如果你是一名正在准备技术面试的开发者,或者是一名计算机专业的学生,那么“刷LeetCode”这个词对你来说一定不陌生。它几乎是通往大厂Offer的必经之路,是无数人投入数百小时甚至上千小时去重复练习的“标准动作”…

2026/8/16 6:11:22

找个大佬带带我

本人10岁,刚学编程没多久,会一点Python和C的基础,能写点简单的脚本。Linux命令会用几个基本的还有batch,服务器有一台,网站也有一个(是AI帮我搞的,我改了点内容)。我最擅长的大概是A…

2026/8/16 6:11:22

ARCH与交叉编译器的关系

编译器只是一台“翻译机”,它自己没有思想,它只会机械地把输入的 .c 文件翻译成机器码。而决定“把哪些 .c 文件喂给它翻译”的,是构建系统(Makefile)关键机制: 当你执行 make 时,总管会先看 AR…

2026/8/16 6:06:21

人工智能训练师(三级/高级工)考试指南

一、职业与考试概述 1.1 什么是人工智能训练师? 人工智能训练师是国家人社部2020年发布的新职业,职业编码为4-04-05-05。其职业定义为:使用智能训练软件,在人工智能产品实际使用过程中进行数据采集、数据处理、数据标注、模型搭…

2026/8/16 0:00:35

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:36

工业传感器与变送器详解:序章 从物理世界到工业数据

序章 从物理世界到工业数据 ——重新认识工业传感器与变送器 工业自动化系统正变得日益复杂。今天的工业现场早已不是简单的控制回路,而是由多层技术共同构成的立体体系:PLC、DCS、SCADA、MES、工业互联网、边缘计算与人工智能。控制系统可以执行复杂算法,工业网络可以实现…

2026/8/16 0:00:35

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:36

工业传感器与变送器详解:序章 从物理世界到工业数据

序章 从物理世界到工业数据 ——重新认识工业传感器与变送器 工业自动化系统正变得日益复杂。今天的工业现场早已不是简单的控制回路,而是由多层技术共同构成的立体体系:PLC、DCS、SCADA、MES、工业互联网、边缘计算与人工智能。控制系统可以执行复杂算法,工业网络可以实现…

2026/8/15 9:46:39

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/15 4:56:16

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/15 9:46:30

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…