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

IO多路复用---EPOLL

一、select、poll、epoll核心特点对比

1、三者均用于监测多个文件描述符(fd)的事件,但在存储方式、性能、功能支持上差异显著,具体对比如下:

对比维度selectpollepoll
文件描述符存储方式位图(数组)链表红黑树(二叉树,内核层维护)
监测fd上限最多1024个无上限无上限(红黑树提升查找效率)
应用层-内核层数据拷贝需反复拷贝(fd集合表)需反复拷贝(fd集合表)无需反复拷贝(fd集合创建在内核层)
事件查找方式需遍历返回的集合表找到达事件需遍历返回的集合表找到达事件直接返回到达的事件,无需遍历
触发模式支持仅水平触发(低速模式)仅水平触发(低速模式)水平触发(低速)+ 边沿触发(高速)

2、水平触发与边沿触发

文档提及“水平触发与边沿触发的区别”,但未展开细节,仅明确:

  • 水平触发(低速模式):select、poll、epoll均支持,适合对实时性要求不高的场景。
  • 边沿触发(高速模式):仅epoll支持,适合高并发、低延迟的高速场景。

二、epoll的具体实现(函数与结构体)

epoll通过3个核心函数完成“创建fd集合-操作fd-监测事件”的流程,配合特定结构体传递事件信息,细节如下:

1. 核心结构体定义

用于描述fd对应的事件类型与关联数据:

// 数据联合体:存储与fd关联的用户数据  
typedef union epoll_data {  void        *ptr;   // 指针(自定义数据)  int          fd;    // 关注的文件描述符(常用)  uint32_t     u32;   // 32位无符号整数  uint64_t     u64;   // 64位无符号整数  
} epoll_data_t;  // epoll事件结构体:描述fd的事件类型与关联数据  
struct epoll_event {  uint32_t     events;      // 事件类型(读/写等)  epoll_data_t data;        // 关联的用户数据(常用data.fd表示关注的fd)  
};  

其中,events支持的核心事件类型:

  • EPOLLIN:读事件(fd可读取数据)
  • EPOLLOUT:写事件(fd可写入数据)

2. epoll工作流程

  1. 调用epoll_create创建内核层fd集合,获取epfd。
  2. 调用epoll_ctlop=EPOLL_CTL_ADD)将需监测的fd及事件添加到集合。
  3. 调用epoll_wait通知内核监测事件,阻塞等待事件到达。
  4. epoll_wait返回后,从events数组中获取到达的事件。
  5. 根据事件类型(读/写)处理对应任务。

3. 核心函数

(1)创建epoll fd集合:epoll_create
  • 功能:通知内核创建一个用于存储fd的集合(内核维护)。
  • 参数size - 计划监测的fd个数(仅为提示,无实际限制)。
  • 返回值
    • 成功:返回代表该集合的文件描述符(epfd)。
    • 失败:返回-1。
(2)操作epoll集合(增/删/改fd):epoll_ctl
  • 功能:对已创建的epoll集合(epfd)进行fd的添加、修改、删除操作。
  • 参数
    • epfd:通过epoll_create创建的集合描述符。
    • op:操作类型(3种):
      • EPOLL_CTL_ADD:向集合添加fd。
      • EPOLL_CTL_MOD:修改集合中已存在fd的事件。
      • EPOLL_CTL_DEL:从集合中删除fd。
    • fd:需要操作的目标文件描述符。
    • event:指向struct epoll_event的指针,描述fd的事件类型与关联数据。
  • 返回值
    • 成功:返回0。
    • 失败:返回-1。
(3)监测事件:epoll_wait
  • 功能:通知内核开始监测epoll集合中fd的事件,阻塞等待事件发生或超时。
  • 参数
    • epfd:需监测的epoll集合描述符。
    • events:指向struct epoll_event数组的指针,用于存储返回的到达事件
    • maxeventsevents数组的最大容量(即最多接收的事件个数)。
    • timeout:超时时间(单位未明确,通常为毫秒):
      • -1:不设置超时,一直阻塞等待事件。
      • 其他值:超时后返回(即使无事件)。
  • 返回值
    • 成功:返回实际到达的事件个数。
    • 失败:返回-1。

4.代码练习

从终端和管道中获取数据并且输出

#include "head.h"#define MAX_FD_CNT 2 int epoll_fd_add(int spfds, int fd, uint32_t events)
{struct epoll_event ev;ev.events = events;ev.data.fd = fd;int ret = epoll_ctl(spfds, EPOLL_CTL_ADD, fd, &ev);if(ret < 0){perror("epoll_ctl error");return -1;}return 0;
}int main(int argc, char const *argv[])
{char buff [1024] = {0};mkfifo("./wrfifo", 0664);int fifofd = open("./wrfifo", O_RDONLY);if(fifofd < 0){perror("open fifo error");return -1;}// 创建集合int epfds = epoll_create(MAX_FD_CNT);if(epfds <0){perror("create error");return -1;}//  封装函数添加epoll_fd_add(epfds, 0, EPOLLIN);epoll_fd_add(epfds, fifofd, EPOLLIN);// 保存到达事件的集合struct epoll_event evs[MAX_FD_CNT];while(1){// 阻塞等待事件到达int cnt = epoll_wait(epfds, evs, MAX_FD_CNT, -1);if(cnt < 0){perror("epoll_wait error");return -1;}for(int i = 0; i <cnt; i++){if(0 == evs[i].data.fd){fgets(buff, sizeof(buff), stdin);printf("STDIN : %s\n", buff);}else if(fifofd == evs[i].data.fd){memset(buff, 0, sizeof(buff));read(evs[i].data.fd, buff, sizeof(buff));printf("FIFO : %s\n", buff);}}}close(fifofd);return 0;
}

向管道中写入数据

#include "head.h"int main(int argc, char const *argv[])
{mkfifo("./wrfifo", 0664);int fd = open("./wrfifo", O_WRONLY);if(fd < 0){perror("open error");return -1;}while(1){write(fd, "China", strlen("China"));sleep(1);}close(fd);return 0;
}

从网络套接字中获取数据

#include "head.h"#define SER_PORT 50000
#define SER_ID "192.168.0.164"
#define MAX_FD_CNT 10086 int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(SER_PORT);seraddr.sin_addr.s_addr = inet_addr(SER_ID);int ret = bind(sockfd, (struct  sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("bind error");return -1;   }ret = listen(sockfd, 10);if(ret < 0){perror("listen error");return -1;}return sockfd;
}int epoll_fd_add(int spfds, int fd, uint32_t events)
{struct epoll_event ev;ev.events = events;ev.data.fd = fd;int ret = epoll_ctl(spfds, EPOLL_CTL_ADD, fd, &ev);if(ret < 0){perror("epoll_ctl error");return -1;}return 0;
}// 删除
int epoll_fd_del(int spfds, int fd, uint32_t events)
{int ret = epoll_ctl(spfds, EPOLL_CTL_DEL, fd, NULL);if(ret < 0){perror("epoll_ctl error");return -1;}return 0;
}int main(int argc, char const *argv[])
{char buff[1024] = {0};struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int sockfd = init_tcp_ser();if(sockfd < 0){return -1;}// 创建集合int epfds = epoll_create(MAX_FD_CNT);if(epfds <0){perror("create error");return -1;}//  封装函数添加epoll_fd_add(epfds, sockfd, EPOLLIN);// 保存到达事件的集合struct epoll_event evs[MAX_FD_CNT];while(1){int cnt = epoll_wait(epfds, evs, MAX_FD_CNT, -1);if(cnt < 0){perror("epoll_wait error");return -1;}for(int i = 0; i < cnt; i++){if(sockfd == evs[i].data.fd){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);if(connfd < 0){perror("accept error");close (sockfd);return -1;}epoll_fd_add(epfds, connfd, EPOLLIN);}else{memset(buff, 0, sizeof(buff));ssize_t num = recv(evs[i].data.fd, buff, sizeof(buff), 0);if(num < 0){perror("recv error");epoll_fd_del(epfds, evs[i].data.fd, EPOLLIN); // i 错误 ,从集合中删除iclose(evs[i].data.fd);              // 先删除再关闭continue;               // 不能使用 break   return}else if (num == 0){epoll_fd_del(epfds, evs[i].data.fd, EPOLLIN);close(evs[i].data.fd);continue;				}printf("%s\n", buff);strcat(buff , "-----> ok");num = send(evs[i].data.fd, buff, sizeof(buff), 0);if(num < 0){perror("send error");epoll_fd_del(epfds, evs[i].data.fd, EPOLLIN); close(evs[i].data.fd);continue;}printf("%s\n", buff);}} }close(sockfd);return 0;
}

    http://www.dtcms.com/a/355016.html

    相关文章:

  1. 把llamafacoty微调后的模型导出ollama模型文件
  2. SPARK入门
  3. Python 多版本环境治理理念驱动的系统架构设计——三维治理、四级隔离、五项自治 原则(路径治理升级修订 V 2.0 版)
  4. 七牛云实践:我们如何用 AIGC 将产品开发从“人想图”变为“图选图”
  5. 使用astah制作专业状态图及C/C++实现解析
  6. 随手小记:elementUI的勾选框使用的坑
  7. 大模型微调示例五之Llama-Factory_agent_functioncalling
  8. 大数据原生集群 (Hadoop3.X为核心) 本地测试环境搭建三
  9. 仓颉编程语言:全场景开发的未来选择
  10. SAP-ABAP:SAP HANA 架构解析:主从(Scale-Out)与主备(High Availability)架构深度对比
  11. 从零开始学习JavaWeb-20
  12. 架构评审:构建稳定、高效、可扩展的技术架构(上)
  13. 刷题日记0828
  14. AMGCL介绍和使用
  15. Spark 安装教程与使用指南
  16. Jetson(meta‑tegra)升级要点与 doflash.sh 组件清单
  17. 嵌入式研发工程师成长路线图,基础入门 → 中级提升 → 高级进阶 → 专家方向
  18. 基于 Spring AMQP 的 RabbitMQ 分布式消息系统实战
  19. imx6ull-驱动开发篇47——Linux SPI 驱动实验
  20. Java全栈工程师的实战面试:从基础到微服务的全面解析
  21. 磁力计校准矩阵求解方法解析
  22. go grpc使用场景和使用示例
  23. python02
  24. Codeforces Round 1043 (Div. 3) F. Rada and the Chamomile Valley
  25. 02Shell的变量运算以及数据比较
  26. 卷积神经网络(一):卷积神经网络基础
  27. 基于卷积神经网络 (CNN) 的 MNIST 手写数字识别模型
  28. 如果给我们直接创建的类加上索引?和len方法?
  29. 深度学习篇---模型参数保存
  30. 卷积神经网络实现mnist手写数字集识别案例