news 2026/5/15 0:57:52

高性能网络设计秘笈:深入剖析Linux网络IO与epoll

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
高性能网络设计秘笈:深入剖析Linux网络IO与epoll

epoll的优点

(1)不需要轮询所有的文件描述符 (2)每次取就绪集合,都在固定位置 (3)事件的就绪和IO触发可以异步解耦

四、epoll函数原型

4.1、epoll_create(int size)

代码语言:javascript

AI代码解释

#include <sys/epoll.h> int epoll_create(int size);

功能:创建epoll的文件描述符。 参数说明:size表示内核需要监控的最大数量,但是这个参数内核已经不会用到,只要传入一个大于0的值即可。 当size<=0时,会直接返回不可用,这是历史原因保留下来的,最早的epoll_create是需要定义一次性就绪的最大数量;后来使用了链表以便便维护和扩展,就不再需要使用传入的参数。 返回:返回该对象的描述符,注意要使用 close 关闭该描述符。

4.2、epoll_ctl

代码语言:javascript

AI代码解释

#include <sys/epoll.h> int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); // epoll_ctl对应系统调用sys_epoll_ctl

功能:操作epoll的文件描述符,主要是对epoll的红黑树节点进行操作,比如节点的增删改查。 参数说明:

参数

含义

epfd

通过 epoll_create 创建的文件描述符

op

对红黑树的操作,比如节点的增加、修改、删除,分别对应EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL

fd

需要添加监听的文件描述符

event

事件信息

4.2.1、event参数说明

struct epoll_event结构体原型

代码语言:javascript

AI代码解释

typedef union epoll_data{ void* ptr; int fd; uint32_t u32; uint64_t u64 }; struct epoll_event{ uint32_t events; epoll_data_t data; }

events成员代表要监听的epoll事件类型 events成员:

成员变量

含义

EPOLLIN

监听fd的读事件

EPOLLOUT

监听fd的写事件

EPOLLRI

监听紧急数据可读事件(带外数据到来)

EPOLLRDHUP

监听套接字关闭或半关闭事件

EPOLLET

将EPOLL设为边缘触发(Edge Triggered)模式

data成员: data 成员是一个联合体类型,可以在调用 epoll_ctl 给 fd 添加/修改描述符监听的事件时携带一些数据,方便后面的epoll_wait可以取出信息使用。

4.2.2、扩展说明:SYSCALL_DEFINE数字 的宏定义

跟着的数字代表函数需要的参数数量,比如SYSCALL_DEFINE1代表函数需要一个参数、SYSCALL_DEFINE4代表函数需要4个参数。

4.2.3、注意

epoll_ctl是非阻塞的,不会被挂起。

4.3、epoll_wait

函数原型

代码语言:javascript

AI代码解释

#include <sys/epoll.h> int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

功能:阻塞一段时间,等待事件发生 返回:返回事件数量,事件集添加到events数组中。也就是遍历红黑树中的双向链表,把双向链表中的节点数据拷贝出来,拷贝完毕后把节点从双向链表中移除。

返回值

含义

大于0

事件个数

等于0

超时时间timeout到了

小于0

出错,可通过errno查看出错原因

参数说明:

参数

含义

epfd

通过 epoll_create 创建的文件描述符

events

存放就绪的事件集合,是输出参数

maxevents

最大可存放事件数量,events数组大小

timeout

阻塞等待的时间长短,单位是毫秒,-1表示一直阻塞等待

五、epoll使用步骤

图片

step 1:创建epoll文件描述符

代码语言:javascript

AI代码解释

int epfd = epoll_create(1);

step 2:创建struct epoll_event结构体

代码语言:javascript

AI代码解释

struct epoll_event ev; ev.data.fd=listenfd;//保存监听的fd,以便epoll_wait的后续操作 ev.events=EPOLLIN;//设置监听fd的可读事件

step 3:添加事件监听

代码语言:javascript

AI代码解释

epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

step 4:等待事件

代码语言:javascript

AI代码解释

struct epoll_event events[EVENTS_LENGTH]; char rbuffer[MAX_BUFF]={ 0 }; char wbuffer[MAX_BUFF]={ 0 }; while(1) { int nready = epoll_wait(epfd,events,EVENTS_LENGTH,-1);//-1表示阻塞等待 int i=0; for(i=0;i<nready;i++) { int clientfd=events[i].data.fd; if(clientfd==listenfd) { struct sockaddr_in client; int len=sizeof(client); int confd=accept(listenfd,(struct sockaddr*)&client,&len); //step 2:创建struct epoll_event结构体 struct epoll_event evt; evt.data.fd=confd;//保存监听的fd,以便epoll_wait的后续操作 evt.events=EPOLLIN;//设置监听fd的可读事件 // step 3:添加事件监听 epoll_ctl(epfd,EPOLL_CTL_ADD,confd,&evt); } else if(events[i].events &EPOLLIN) { int ret = recv(clientfd,rbuffer,MAX_BUFF,0); if(ret>0) { rbuffer[ret]='\0';//剔除干扰数据 printf("recv: %s\n",rbuffer); memcpy(wbuffer,rbuffer,MAX_BUFF);//拷贝数据,做回传示例 //step 2:创建struct epoll_event结构体 struct epoll_event evt; evt.data.fd=clientfd;//保存监听的fd,以便epoll_wait的后续操作 evt.events=EPOLLOUT;//设置监听fd的可写事件 // step 3:修改事件监听 epoll_ctl(epfd,EPOLL_CTL_MOD,clientfd,&evt); } } else if(events[i].events &EPOLLOUT) { int ret = send(clientfd,wbuffer,MAX_BUFF,0); printf("send: %s\n",wbuffer); //step 2:创建struct epoll_event结构体 struct epoll_event evt; evt.data.fd=clientfd;//保存监听的fd,以便epoll_wait的后续操作 evt.events=EPOLLIN;//设置监听fd的可读事件 // step 3:修改事件监听 epoll_ctl(epfd,EPOLL_CTL_MOD,clientfd,&evt); } } }

六、完整示例代码

代码语言:javascript

AI代码解释

#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <fcntl.h> #include <unistd.h> #include <pthread.h> #include <sys/epoll.h> #include <string.h> #define BUFFER_LENGTH 128 #define EVENTS_LENGTH 128 char rbuff[BUFFER_LENGTH] = { 0 }; char wbuff[BUFFER_LENGTH] = { 0 }; int main() { // block int listenfd = socket(AF_INET, SOCK_STREAM, 0); // if (listenfd == -1) return -1; // listenfd struct sockaddr_in servaddr; servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(9999); if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) { return -2; } #if 0 // nonblock int flag = fcntl(listenfd, F_GETFL, 0); flag |= O_NONBLOCK; fcntl(listenfd, F_SETFL, flag); #endif listen(listenfd, 10); int epfd = epoll_create(1); struct epoll_event ev, events[EVENTS_LENGTH]; ev.events = EPOLLIN; ev.data.fd = listenfd; epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); printf("epfd : %d\n", epfd); while (1) { int nready = epoll_wait(epfd, events, EVENTS_LENGTH, -1); printf("nready --> %d\n",nready); int i; for (i = 0; i < nready;i++) { int clientfd = events[i].data.fd; if (listenfd == clientfd) { // accept struct sockaddr_in client; int len = sizeof(client); int conffd = accept(clientfd, (struct sockaddr*)&client,&len); printf("conffd --> %d\n",conffd); ev.events = EPOLLIN; ev.data.fd = conffd; epoll_ctl(epfd, EPOLL_CTL_ADD, conffd, &ev); } else if(events[i].events & EPOLLIN)//client { int ret=recv(clientfd, rbuff, BUFFER_LENGTH, 0); if (ret > 0) { rbuff[ret] = '\0'; printf("recv buffer: %s\n", rbuff); /* int j; for (j = 0; j < BUFFER_LENGTH;j++) { buff[j] = 'a' + (j % 26); } send(clientfd, buff, BUFFER_LENGTH, 0); */ memcpy(wbuff, rbuff, BUFFER_LENGTH); ev.events = EPOLLOUT; ev.data.fd = clientfd; epoll_ctl(epfd, EPOLL_CTL_MOD, clientfd, &ev); } } else if (events[i].events & EPOLLOUT) { send(clientfd, wbuff, BUFFER_LENGTH, 0); printf("send --> %s\n",wbuff); ev.events = EPOLLIN; ev.data.fd = clientfd; epoll_ctl(epfd, EPOLL_CTL_MOD, clientfd, &ev); } } } return 0; }

七、epoll的缺点

读写使用相同的缓冲区。比如上述的示例中,wbuffer和rbuffer是使用同一个缓冲区的,所以需要rbuff[ret] = '\0';去除杂数据。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/15 0:55:23

树莓派SPI OLED屏驱动指南:从硬件连接到Python编程实战

1. 项目概述与核心价值如果你手头有一块树莓派&#xff0c;并且对让它“开口说话”或者显示点什么东西感兴趣&#xff0c;那么给树莓派外接一块显示屏绝对是个能立刻带来成就感的选择。在众多显示屏里&#xff0c;OLED&#xff08;有机发光二极管&#xff09;屏以其独特的魅力脱…

作者头像 李华
网站建设 2026/5/15 0:53:57

企业内网应用安全调用大模型Taotoken访问控制与审计日志实践

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 企业内网应用安全调用大模型&#xff1a;Taotoken访问控制与审计日志实践 当企业决定将大模型能力集成到内部应用时&#xff0c;安…

作者头像 李华
网站建设 2026/5/15 0:53:46

观察Taotoken在多模型间路由切换的响应速度与成功率

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 观察Taotoken在多模型间路由切换的响应速度与成功率 当你的应用依赖大模型API时&#xff0c;服务的连续性和响应速度至关重要。Tao…

作者头像 李华
网站建设 2026/5/15 0:52:15

自托管代码仓库聚合分析平台CodeStacker:架构设计与部署指南

1. 项目概述&#xff1a;一个为开发者打造的代码仓库聚合与智能分析工具如果你和我一样&#xff0c;每天需要面对GitHub、GitLab、Bitbucket等不同平台上的几十个甚至上百个代码仓库&#xff0c;那么“仓库管理”这件事本身&#xff0c;可能就已经消耗了你大量的精力。哪个项目…

作者头像 李华
网站建设 2026/5/15 0:51:11

Linux watch 命令深度解析:从实时监控到变化检测的完整实现

watch 的核心原理 watch 的本质很简单&#xff1a;循环执行命令 全屏显示输出。但它的实现细节值得深挖。 底层实现机制 // watch 的简化实现逻辑 int main(int argc, char **argv) {while (1) {clear_screen(); // 清屏print_header(); // 显示标题栏sy…

作者头像 李华
网站建设 2026/5/15 0:50:58

Python量化交易实战:从数据获取到策略回测的完整指南

1. 项目概述与核心价值如果你对金融市场感兴趣&#xff0c;同时又是一名Python开发者&#xff0c;那么“Nikhil-Adithyan/Algorithmic-Trading-with-Python”这个项目绝对值得你花时间深入研究。这不是一个简单的脚本合集&#xff0c;而是一个系统性的、旨在用代码构建自动化交…

作者头像 李华