IO复用之epoll模型

什么是epoll

epoll 是 Linux 操作系统提供的一种高性能的事件通知机制,用于处理大量文件描述符上的事件。它是一种 I/O 事件通知机制,通常用于处理网络编程中的并发连接。

在传统的 I/O 模型中,程序通常使用 select 或 poll 函数来等待多个文件描述符上的事件。这种方式有一些性能上的限制,特别是当需要监视的文件描述符数量很大时。epoll 的设计旨在解决这些性能问题。

以下是一些关键的 epoll 特性:

高性能: epoll 使用红黑树(RB tree)来存储文件描述符,这允许它在处理大量描述符时保持较低的时间复杂度,从而提高性能。

事件驱动: epoll 使用事件驱动的模型,即当文件描述符上有事件发生时,内核会通知应用程序。这避免了应用程序不断地轮询文件描述符,从而提高效率。

支持边缘触发和水平触发: epoll 可以以边缘触发(edge-triggered)或水平触发(level-triggered)的方式工作。边缘触发模式表示只有在文件描述符上的状态变化时才会通知应用程序,而水平触发模式表示只要文件描述符上有事件发生,就会通知应用程序。

支持单个系统调用处理多个事件: epoll 允许在一个系统调用中处理多个事件,这减少了系统调用的次数,提高了效率。

使用 epoll 可以更有效地管理大量并发连接,适用于服务器应用程序,特别是网络服务器,以提高性能和可伸缩性。在实际应用中,epoll 通常与非阻塞 I/O 配合使用,以更好地处理并发请求。

Epoll的三大函数:epoll_create,epoll_wait, epoll_ctl

epoll_create

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

成功时返回epoll文件描述符,失败时返回-1。

  • size:epoll实例的大小。
  • 该函数从2.3.2版本的开始加入的,2.6版开始引入内核
  • Linux最新的内核稳定版本已经到了5.8.14,长期支持版本到了5.4.70
  • 从2.6.8内核开始的Linux,会忽略这个参数,但是必须要大于0
  • 这个是Linux独有的函数

epoll_ctl

#include<sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);

成功时返回0,失败时返回-1。

  • epfd 用于注册监视对象的epoll例程的文件描述符。
  • op 用于指定监视对象的添加、删除或更改等操作。

EPOLL_CTL_ADD,EPOLL_CTL_MOD,EPOLL_CTL_DEL

  • fd 需要注册的监视对象文件描述符。

event 监视对象的事件类型。
EPOLLIN∶需要读取数据的情况。
EPOLLOUT∶输出缓冲为空,可以立即发送数据的情况。
EPOLLPRI∶收到OOB数据的情况。
EPOLLRDHUP∶断开连接或半关闭的情况,这在边缘触发方式下非常有用。
EPOLLERR∶发生错误的情况。
EPOLLET∶以边缘触发的方式得到事件通知。
EPOLLONESHOT∶发生一次事件后,相应文件描述符不再收到事件通知。因此需要向
epoll_ctl函数的第二个参数传递
EPOLLCTL_MOD,再次设置事件

epoll_wait:

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

成功时返回发生事件的文件描述符数,失败时返回-1。

  • epfd 表示事件发生监视范围的epol例程的文件描述符
  • events 保存发生事件的文件描述符集合的结构体地址值。
  • maxevents 第二个参数中可以保存的最大事件数目。
  • Timeout:以1/1000秒为单位的等待时间,传递-1时,一直等待直到发生事件。

Epoll实战案例

基于eopll实现简单的文件上传下载功能,仅服务端代码

#include <cstdio>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include<pthread.h>
//#include<sys/types.h>
//#include<sys/stat.h>
#include <fcntl.h>
#include <sys/file.h>
#include <unordered_map>#define EPOLL_EVENT_SIZE 100
#define USER_NUM 100// 文件存放路径
const char* PATH = "/root/projects/chatdata/";struct Data
{char typ;// 请求类型char content[1024]; // 请求内容
};
void error_handling(const char* msg) {fputs(msg, stderr);fputc('\n', stderr);exit(-1);
}// 一个线程专门用来上传下载文件
void* thread_main(void* arg)
{//一个map管理文件描述符std::unordered_map<int, int> fdMap;pthread_detach(pthread_self());char filePath[1024];char buf[sizeof(Data)];memset(&buf, 0, sizeof(buf));// 准备套接字int serv_sock, clnt_sock;sockaddr_in serv_adr, clnt_adr;socklen_t client_sz;client_sz = sizeof(clnt_adr);serv_sock = socket(PF_INET, SOCK_STREAM, 0);serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(9091);if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1) {error_handling("bind() error");close(serv_sock);return 0;}if (listen(serv_sock, 5) == -1) {error_handling("listen() error");close(serv_sock);return 0;}epoll_event event;memset(&event, 0, sizeof(event));int epfd, event_cnt;epfd = epoll_create(1);if (epfd == -1) {error_handling("epoll_create() error");close(serv_sock);return 0;}epoll_event* all_events = new epoll_event[EPOLL_EVENT_SIZE];event.events = EPOLLIN;event.data.fd = serv_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);while (true) {event_cnt = epoll_wait(epfd, all_events, EPOLL_EVENT_SIZE, 1000);if (event_cnt == -1) {printf("epoll_wait() error");break;}if (event_cnt == 0)continue;for (int i = 0; i < event_cnt; i++) {// 监听可读事件if (all_events[i].events & EPOLLIN) {// 当发生的事件为服务器时接收接收连接if (all_events[i].data.fd == serv_sock) {clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &client_sz);// TODO  修改缓存区,娜迦算法if (clnt_sock == -1) {printf("accept() error");continue;}event.events = EPOLLIN;event.data.fd = clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);}else{ssize_t len = read(all_events[i].data.fd, buf, sizeof(buf));if (len <= 0) {// 取消监听,关闭文件操作符,关闭客户端sockepoll_ctl(epfd, EPOLL_CTL_DEL, all_events[i].data.fd, NULL);close(fdMap[all_events[i].data.fd]);close(all_events[i].data.fd);fdMap.erase(all_events[i].data.fd);printf("client is close! %d\n", clnt_sock);}else{if (buf[0] == 0) {//首次接收上传文件请求,打开文件memset(filePath, 0, sizeof(filePath));//const char* PATH = "/root/projects/chatdata/";strcpy(filePath, PATH);strcat(filePath, buf + 1);printf("filePath %s\n", filePath);int fd = open(filePath, O_WRONLY | O_CREAT | O_APPEND, 0777);perror("open");fdMap[all_events[i].data.fd] = fd;}else if (buf[0] == 1){// 没有考虑文件终止问题EOFwrite(fdMap[all_events[i].data.fd], buf + 1, len - sizeof(buf[0]));}else if (buf[0] == 2){// 用户请求下载文件memset(filePath, 0, sizeof(filePath));//const char* PATH = "/root/projects/chatdata/";strcpy(filePath, PATH);strcat(filePath, buf + 1);int fd = open(filePath, O_RDONLY);printf("filePath %s\n", filePath);printf("int fd = open(filePath, O_RDONLY); %d\n", fd);perror("open");unsigned long fileSize = static_cast<unsigned long>(lseek(fd, 0, SEEK_END));printf("unsigned long fileSize = static_cast<unsigned long>(lseek(fd, 0, SEEK_END)); %d\n", fileSize);lseek(fd, 0, SEEK_SET);memset(&buf, 0, sizeof(buf));memcpy(buf, &fileSize, sizeof(fileSize));//printf("memset(&buf, 0, sizeof(buf)); %d\n", buf);fdMap[all_events[i].data.fd] = fd;write(all_events[i].data.fd, buf, sizeof(buf));//修改监听的事件clnt_sock = all_events[i].data.fd;event.events = EPOLLOUT;event.data.fd = clnt_sock;epoll_ctl(epfd, EPOLL_CTL_MOD, clnt_sock, &event);}}}}// 监听可写事件if (all_events[i].events & EPOLLOUT) {//开写int len = read(fdMap[all_events[i].data.fd], buf, sizeof(buf));write(all_events[i].data.fd, buf, len);if (len < sizeof(buf)) {// 写完了epoll_ctl(epfd, EPOLL_CTL_DEL, all_events[i].data.fd, NULL);close(fdMap[all_events[i].data.fd]);close(all_events[i].data.fd);fdMap.erase(all_events[i].data.fd);printf("client is close! %d\n", clnt_sock);}}}}delete all_events;close(serv_sock);pthread_exit(0);
}int main()
{pthread_t t_id;int thread_param = 5;//thread_param表示函数传参 , thread_main表示线程函数if (pthread_create(&t_id, NULL, thread_main, (void*)&thread_param) != 0){puts("pthread_create() error");return -1;};// TODO 其他的业务逻辑return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/651127.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot+vue的校园资料分享平台(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

数据灾难恢复:应对._locked勒索病毒的有效方法

导言&#xff1a; 近年来&#xff0c;网络犯罪愈发猖獗&#xff0c;其中一种威胁备受关注——._locked勒索病毒。这种恶意软件的攻击方式主要通过加密用户的数据文件&#xff0c;随后勒索受害者以解密密钥为代价。在这篇文章中&#xff0c;我们将介绍._locked勒索病毒的特点、…

qt学习:实战 http请求获取qq的吉凶

目录 利用的api是 聚合数据 的qq号码测吉凶 编程步骤 配置ui界面 添加头文件&#xff0c;定义网络管理者和http响应槽函数 在界面的构造函数里创建管理者对象&#xff0c;关联http响应槽函数 实现按钮点击事件 实现槽函数 效果 利用的api是 聚合数据 的qq号码测吉凶 先…

2024 高级前端面试题之 CSS 「精选篇」

该内容主要整理关于 CSS 的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 CSS模块精选篇 1. 盒模型2. BFC3. 层叠上下文4. 居中布局5. 选择器权重计算方式6. 清除浮动7. link 与 import 的区别8. CSS3的新特性9. CSS动画和过渡10. 有哪些…

LabVIEW继电器触点接触电阻自动测试

继电器作为工业中的重要组件&#xff0c;其性能直接影响着整个生产线的可靠性和安全性。触点接触电阻是衡量继电器性能的重要参数&#xff0c;传统的测试方法效率低下且成本高昂。为了解决这些问题&#xff0c;采用LabVIEW软件&#xff0c;结合专业的硬件平台&#xff0c;实现了…

OceanMind海睿思入选《2023大数据产业年度创新技术突破奖》,并蝉联多项图谱

近日&#xff0c;由数据猿和上海大数据联盟主办&#xff0c;上海市经济和信息化委员会、上海市科学技术委员会指导的“第六届金猿季&魔方论坛——大数据产业发展论坛”在上海成功举行&#xff0c;吸引了数百位业界精英的参与。中新赛克海睿思作为国内数字化转型优秀厂商代表…

用C语言实现贪吃蛇游戏!!!(破万字)

前言 大家好呀&#xff0c;我是Humble&#xff0c;不知不觉在CSND分享自己学过的C语言知识已经有三个多月了&#xff0c;从开始的C语言常见语法概念说到C语言的数据结构今天用C语言实现贪吃蛇已经有30余篇博客的内容&#xff0c;也希望这些内容可以帮助到各位正在阅读的小伙伴…

【 C++私房菜】模板的入门与进阶

目录 一、模板的定义 a.函数模板的调用 b.类模板的定义 2、模板的重载 3、非类型模板参数和模板类型参数 4、模板的编译 二、模板的特化 1、函数模板特化 2、类模板特化 a.全特化 b.偏特化 三、模板相关定义 一、模板的定义 a.函数模板的调用 理在的 C编译器实现…

头歌C++之For循环性质编程实训

目录 第1关:求1到n间所有整数的和 本关必读 本关任务 测试说明 第2关:求s=a+aa+aaa+aaaa+aa...a的值 本关必读 本关任务 测试说明 第3关:求1!+2!+3!+⋯+n!的值 本关必读 本关任务

确定软件项目范围基准 5个重点

软件项目范围基准明确了项目的边界、目标和主要交付成果&#xff0c;有助于提高项目成本、进度和资源估算的准确性&#xff0c;便于实施项目控制&#xff0c;而且还可以帮助我们清楚分派责任&#xff0c;防止范围蔓延&#xff0c;从而提升项目的成功率。 如果没有明确确定范围基…

Pointnet++改进优化器系列:全网首发AdamW优化器 |即插即用,实现有效涨点

简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入AdamW优化器,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二 2.3 步

网络基础---初识网络

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、局域网…

算法训练营Day59(单调栈)

下一个更大元素II 503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 最直接的方法&#xff0c;我自己写的。。 class Solution {public int[] nextGreaterElements(int[] nums) {int len nums.length*2;int [] nums2 new int[len];for(int i 0;i<len;i…

Nav2笔记

1、源码安装 git clone https://github.com/ros-planning/navigation2.git -b humble 1.1 一键安装依赖 wget http://fishros.com/install -O fishros && . fishros rosdepc install -r --from-paths src --ignore-src --rosdistro $ROS_DISTRO -y 1.2 编译 colc…

动态规划学习——背包问题

问题&#xff1a; 有一个背包&#xff0c;有最大的可以承受的重量Weight 有一些物品&#xff0c;每个物品都有相应的重量和价值 给两个数组w[]和v[]&#xff0c;其中w[i]表示第i个物品的重量&#xff0c;v[i]表示第i个物品的价值 求如何拿才能在不超过背包承重的情况下拿到的最…

Hive之set参数大全-16

配置 HiveServer2 中 Tez Workload Manager (WM) Application Master (AM) 注册的超时时间 在 Hive 中&#xff0c;hive.server2.tez.wm.am.registry.timeout 是一个参数&#xff0c;用于配置 HiveServer2 中 Tez Workload Manager (WM) Application Master (AM) 注册的超时时…

高斯分布的应用,正态分布的实践应用,什么是极大似然估计法

目录 高斯分布的应用 正态分布的实践应用 什么是极大似然估计法 高斯分布的应用

【leetcode刷刷】235. 二叉搜索树的最近公共祖先 、701.二叉搜索树中的插入操作 、450.删除二叉搜索树中的节点

235. 二叉搜索树的最近公共祖先 class Solution:def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:# 递归if not root: return if root.val p.val: return pif root.val q.val: return qleft Noneright Noneif root.val > p.…

LeetCode2859. Sum of Values at Indices With K Set Bits

文章目录 一、题目二、题解 一、题目 You are given a 0-indexed integer array nums and an integer k. Return an integer that denotes the sum of elements in nums whose corresponding indices have exactly k set bits in their binary representation. The set bits…

前后端交互—使用自己的服务器开发项目

代码下载 完善服务器 对于上一篇博客开发的服务端项目&#xff0c;还需要增加文章列表查询&#xff0c;文章删除&#xff0c;文章更新三个接口。 文章列表查询接口 验证表单数据 在路径 schema/user.js 中增加 articles 验证表单数据&#xff0c;其中使用的 .allow() 表示…