28-LINUX--I/O复用-epoll

一.epoll概述

        epoll 是 Linux 特有的 I/O 复用函数。它在实现和使用上与 select、poll 有很大差异。首
先,epoll 使用一组函数来完成任务,而不是单个函数。其次,epoll 把用户关心的文件描述
符上的事件放在内核里的一个事件表中。从而无需像 select 和 poll 那样每次调用都要重复传
入文件描述符或事件集。但 epoll 需要使用一个额外的文件描述符,来唯一标识内核中的这
个事件表。epoll 相关的函数如下:
        ◼ epoll_create()用于创建内核事件表
        ◼ epoll_ctl()用于操作内核事件表,增加,删除,修改内核事件表
        ◼ epoll_wait()用于在一段超时时间内等待一组文件描述符上的事件;监听事件描述符,获取就绪的事件描述符,执行相应处理

1.epoll接口

 #include <sys/epoll.h>int epoll_create(int size);/*epoll_create()成功返回内核事件表的文件描述符,失败返回-1size 参数现在并不起作用,只是给内核一个提示,告诉它事件表需要多大。*/int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);/*epoll_ctl()成功返回 0,失败返回-1epfd 参数指定要操作的内核事件表的文件描述符fd 参数指定要操作的文件描述符op 参数指定操作类型:EPOLL_CTL_ADD 往内核事件表中注册 fd 上的事件EPOLL_CTL_MOD 修改 fd 上的注册事件EPOLL_CTL_DEL 删除 fd 上的注册事件event 参数指定事件,它是 epoll_event 结构指针类型,epoll_event 的定义如下:struct epoll_event{_uint32_t events; // epoll 事件epoll_data_t data; // 用户数据};其中,events 成员描述事件类型,epoll 支持的事件类型与 poll 基本相同,表示
epoll 事件的宏是在 poll 对应的宏前加上‘E’,比如 epoll 的数据可读事件是
EPOLLIN。但是 epoll 有两个额外的事件类型--EPOLLET 和 EPOLLONESHOT。
data 成员用于存储用户数据,是一个联合体,其定义如下:typedef union epoll_data{void *ptr;int fd;uint32_t u32;uint64_t u64;}epoll_data_t;其中 fd 成员使用的最多,它指定事件所从属的目标文件描述符。*/int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);/*epoll_wait()成功返回就绪的文件描述符的个数,失败返回-1,超时返回 0epfd 参数指定要操作的内核事件表的文件描述符events 参数是一个用户数组,这个数组仅仅在 epoll_wait 返回时保存内核检测到
的所有就绪事件,而不像 select 和 poll 的数组参数那样既用于传入用户注册的事
件,又用于输出内核检测到的就绪事件。这就极大地提高了应用程序索引就绪文件
描述符的效率。maxevents 参数指定用户数组的大小,即指定最多监听多少个事件,它必须大于0timeout 参数指定超时时间,单位为毫秒,如果 timeout 为 0,则 epoll_wait 会立即
返回,如果 timeout 为-1,则 epoll_wait 会一直阻塞,直到有事件就绪。*/

2.epoll的两种模式

        epoll 对文件描述符有两种操作模式:LT(Level Trigger,电平触发)模式和 ET(Edge
Trigger,边沿触发)模式。LT 模式是默认的工作模式。当往 epoll 内核事件表中注册一个文
件描述符上的 EPOLLET 事件时,epoll 将以高效的 ET 模式来操作该文件描述符。

2.1 LT模式

        当服务器端不能完全接受客户端发送的数据时,如果服务器端无法一次性处理完,服务器回持续处理提醒;

         对于 LT 模式操作的文件描述符,当 epoll_wait 检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。这样,当应用程序下一次调用 epoll_wait 时,还会再次向应用程序通告此事件,直到该事件被处理。

2.2 ET模式

        事件就绪后,只提醒一次,指导下次事件就绪再次提醒,指导内存缓冲区的数据被完全处理
        对于 ET 模式操作的文件描述符,当 epoll_wait 检测到其上有事件发生并将此事件通知应用程序后,应用程序必须立即处理该事件,因为后续的 epoll_wait 调用将不再向应用程序通知这一事件。所以 ET 模式在很大程度上降低了同一个 epoll 事件被重复触发的次数,因此效率比 LT 模式高。

二.epoll代码

1.epoll ——LT代码

epoll.c
#include<stdio.h>      // 标准输入输出库
#include<stdlib.h>     // 标准库,提供一些通用函数
#include<string.h>    // 字符串操作库
#include<unistd.h>    // UNIX 标准函数库
#include<sys/socket.h> // 套接字相关函数
#include<netinet/in.h> // 网络接口的头文件,提供一些网络编程需要的宏和数据结构
#include<arpa/inet.h>  // 提供inet_addr等函数,用于将点分十进制IP地址转换为网络字节顺序
#include<sys/epoll.h>  // 用于epoll相关的函数声明#define MAXFD 10       // 定义最大文件描述符数量// 初始化socket并绑定到端口
int socket_init()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字if(sockfd == -1) // 如果创建失败{return -1;}struct sockaddr_in saddr; // 定义地址结构memset(&saddr, 0, sizeof(saddr)); // 初始化地址结构saddr.sin_family = AF_INET; // 地址族,使用IPv4saddr.sin_port = htons(6000); // 端口号,使用网络字节顺序saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP地址int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 绑定地址if(res == -1) // 如果绑定失败{printf("bind err\n");return -1;}if(listen(sockfd, 5) == -1) // 开始监听,设置队列长度为5{return -1;}return sockfd; // 返回套接字描述符
}// 向epoll事件表中添加描述符
void epoll_add(int epfd, int fd)
{struct epoll_event ev;ev.data.fd = fd;ev.events = EPOLLIN; // 只关心读事件if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) // 添加描述符{printf("epoll ctl add err\n");}
}// 从epoll事件表中删除描述符
void epoll_del(int epfd, int fd)
{if(epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) == -1) // 删除描述符{printf("epoll ctl del err\n");}
}// 接受客户端连接请求
void accept_cli(int sockfd, int epfd)
{int c = accept(sockfd, NULL, NULL); // 接受连接if(c < 0){return;}printf("accept c=%d\n", c);epoll_add(epfd, c); // 将新的连接添加到epoll事件表
}// 接收客户端发送的数据
void recv_data(int c, int epfd)
{char buff[128] = {0}; // 定义接收缓冲区int n = recv(c, buff, 127, 0); // 接收数据if(n <= 0) // 如果接收到的数据小于等于0{printf("cli close=%d\n", c);epoll_del(epfd, c); // 从epoll事件表中删除该描述符close(c); // 关闭连接return;}printf("buff(%d)=%s\n", c, buff); // 打印接收到的数据send(c, "ok", 2, 0); // 向客户端发送确认消息
}int main()
{int sockfd = socket_init(); // 初始化socketif(sockfd == -1){exit(1); // 如果初始化失败,退出程序}int epfd = epoll_create(MAXFD); // 创建epoll实例if(epfd == -1){exit(1); // 如果创建失败,退出程序}epoll_add(epfd, sockfd); // 将服务器监听的套接字添加到epoll事件表struct epoll_event evs[MAXFD]; // 定义事件数组while(1) // 无限循环,等待事件{int n = epoll_wait(epfd, evs, MAXFD, 5000); // 等待事件,超时时间5000msif(n == -1) // 如果等待失败{printf("epoll wait err\n");}else if(n == 0) // 如果超时没有事件发生{printf("time out\n");}else // 如果有事件发生{for(int i = 0; i < n; i++) // 遍历所有事件{if(evs[i].events & EPOLLIN) // 如果事件是读事件{if(evs[i].data.fd == sockfd) // 如果是监听套接字{accept_cli(sockfd, epfd); // 接受新的连接}else // 如果是已连接的客户端{recv_data(evs[i].data.fd, epfd); // 接收数据}}}}}
}

2.epoll_ET代码

epoll_et.c
#include <stdio.h>      // 标准输入输出库
#include <stdlib.h>     // 标准库,提供动态内存分配等
#include <string.h>    // 字符串操作库
#include <unistd.h>    // UNIX标准函数库,提供close函数等
#include <sys/socket.h>// 套接字库
#include <netinet/in.h> // 网络头文件,提供IPv4地址格式
#include <arpa/inet.h> // 网络地址转换库
#include <sys/epoll.h> // epoll库
#include <errno.h>     // 错误号库
#include <fcntl.h>     // 文件控制库#define MAXFD 10       // 定义最大的文件描述符数量// 设置非阻塞函数
void setnonblock(int fd) {int oldfl = fcntl(fd, F_GETFL); // 获取文件描述符的原有属性int newfl = oldfl | O_NONBLOCK; // 设置非阻塞属性if (fcntl(fd, F_SETFL, newfl) == -1) {printf("fcntl err\n"); // 打印错误信息}
}// 初始化socket函数
int socket_init() {// ... 省略的代码 ...
}// 向epoll内核事件表添加文件描述符
void epoll_add(int epfd, int fd) {struct epoll_event ev;ev.data.fd = fd;ev.events = EPOLLIN | EPOLLET; // 设置为ET模式,并关注读事件setnonblock(fd); // 设置文件描述符为非阻塞if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) {printf("epoll ctl add err\n"); // 打印错误信息}
}// 从epoll内核事件表删除文件描述符
void epoll_del(int epfd, int fd) {if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) == -1) {printf("epoll ctl del err\n"); // 打印错误信息}
}// 接受客户端连接并添加到epoll事件表
void accept_cli(int sockfd, int epfd) {// ... 省略的代码 ...
}// 接收客户端数据
void recv_data(int c, int epfd) {while (1) {char buff[128] = {0};int n = recv(c, buff, sizeof(buff), 0); // 接收数据if (n == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) {send(c, "ok", 2, 0); // 发送确认消息} else {printf("recv err\n"); // 打印错误信息}break;} else if (n == 0) {printf("close(%d)\n", c); // 打印关闭连接信息epoll_del(epfd, c); // 从epoll事件表删除break;} else {printf("buff(%d)=%s\n", c, buff); // 打印接收到的数据}}
}// 主函数
int main() {int sockfd = socket_init(); // 初始化socketif (sockfd == -1) {exit(1); // 如果初始化失败,退出程序}int epfd = epoll_create(MAXFD); // 创建epoll实例if (epfd == -1) {exit(1); // 如果创建失败,退出程序}epoll_add(epfd, sockfd); // 将监听的socket添加到epoll事件表struct epoll_event evs[MAXFD]; // 定义epoll事件数组while (1) { // 无限循环等待事件int n = epoll_wait(epfd, evs, MAXFD, 5000); // 等待事件,超时时间5000msif (n == -1) {printf("epoll wait err\n"); // 打印错误信息} else if (n == 0) {printf("time out\n"); // 打印超时信息} else {for (int i = 0; i < n; i++) { // 遍历所有触发的事件if (evs[i].events & EPOLLIN) { // 如果事件类型为读事件if (evs[i].data.fd == sockfd) {accept_cli(sockfd, epfd); // 接受新的客户端连接} else {recv_data(evs[i].data.fd, epfd); // 接收数据}}}}}
}

三.select、poll 、epoll三者之间的区别

1.select、poll、epoll 区别

1.1. 支持一个进程所能打开的最大连接数

        select:单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。

        poll:poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的

        epoll:虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接

1.5.3. 消息传递方式

select:内核需要将消息传递到用户空间,都需要内核拷贝动作
poll:同上
epoll:epoll通过内核和用户空间共享一块内存来实现的。

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

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

相关文章

mysql (事物)

一.什么是事物 事物是一组操作的集合&#xff0c;不可分割的工作单位&#xff0c;事物会把所有的操作当作一个整体一起向系统提交或撤销操作请求&#xff0c;就是这些操作要么一起成功要么一起失败。 二.事物操作 &#xff08;这个就是一个理解&#xff09; 1.事务特性 原子性…

超详解——python数字和运算——小白篇

目录 1.位运算 2. 常用内置函数/模块 math模块&#xff1a; random模块&#xff1a; decimal模块&#xff1a; 3.内置函数&#xff1a; 总结&#xff1a; 1.位运算 位运算是对整数在内存中的二进制表示进行操作。Python支持以下常见的位运算符&#xff1a; 按位与&…

C语言王国——数据的内存管理

目录 一、引言 二、整形在内存中的存储 2.1 进制之间的转换 2.1.1 整形的二进制 2.1.2 十进制和二进制 2.1.3 十进制和八进制的转换 2.1.4 十六进制和十进制的转换 2.2 原码&#xff0c;反码&#xff0c;和补码 三、大、小端字节序 3.1 大小端的定义 3.2 为什么会有大…

pxe批量部署linux介绍

1、PXE批量部署的作用及必要性&#xff1a; 1&#xff09;智能实现操作系统的批量安装&#xff08;无人值守安装&#xff09;2&#xff09;减少管理员工作&#xff0c;提高工作效率3&#xff09;可以定制操作系统的安装流程a.标准流程定制(ks.cfg)b.自定义流程定制(ks.cfg(%pos…

LLVM Cpu0 新后端8 尾调用优化 Stack Overflow Exception异常

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1V_tZkt9uvxo5bnUufhMQ_Q?…

【iOS】JSONModel源码阅读笔记

文章目录 前言一、JSONModel使用二、JSONModel其他方法转换属性名称 三、源码分析- (instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err[self init]__setup____inspectProperties - (BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMa…

android集成百度文心一言实现对话功能,实战项目讲解,人人都能拥有一款ai应用

大家好&#xff0c;今天给大家讲解下如何实现一个基于百度文心一言的app功能&#xff0c;app内部同时集成了讯飞的语音识别。本文适用于有android基础的小伙伴阅读&#xff0c;文章末尾放上本项目用到的全部实例代码&#xff0c;在使用前请务必看完本文章。 先来给大家看看效果…

微信小程序毕业设计-医院挂号系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

Linux 36.3 + JetPack v6.0@jetson-inference之图像分类

Linux 36.3 JetPack v6.0jetson-inference之图像分类 1. 源由2. imagenet2.1 命令选项2.2 下载模型2.3 操作示例2.3.1 单张照片2.3.2 视频 3. 代码3.1 Python3.2 C 4. 参考资料5. 补充5.1 第一次运行模型本地适应初始化5.2 samba软连接 1. 源由 从应用角度来说&#xff0c;图…

CAN协议简介

协议简介 can协议是一种用于控制网络的通信协议。它是一种基于广播的多主机总线网络协议&#xff0c;常用于工业自动化和控制领域。can协议具有高可靠性、实时性强和抗干扰能力强的特点&#xff0c;被广泛应用于汽车、机械、航空等领域。 can协议采用了先进的冲突检测和错误检测…

Application Load Balancer-ALB

Application Load Balancer-ALB 什么是ALB开通ALB服务实现IPv4服务的负载均衡创建ALB实例创建服务器组添加后端服务器配置监听设置域名解析&#xff08;可选&#xff09;释放ALB实例 什么是ALB 在介绍ALB之前首先介绍一下负载均衡SLB&#xff0c;可以说SLB是负载均衡家族之首 …

ubuntu20.04 安装OpenSSL 1.0.2o (借助腾讯AI完全OK)

文章目录 ubuntu20.04安装openssl-1.0.2o安装后看不到版本信息如何解决 腾讯云 AI 代码助手: 要确认 Linux 开发板的 CPU 是多少位的&#xff0c;可以使用以下方法&#xff1a; 打开终端。输入以下命令&#xff0c;然后按回车键&#xff1a; cat /proc/cpuinfo这将显示关于 CP…

Elastic Search(ES)Java 入门实操(3)数据同步

基本概念和数据查询代码&#xff1a; Elastic Search &#xff08;ES&#xff09;Java 入门实操&#xff08;1&#xff09;下载安装、概念-CSDN博客 Elastic Search&#xff08;ES&#xff09;Java 入门实操&#xff08;2&#xff09;搜索代码-CSDN博客 想要使用 ES 来查询数…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第37课-自动切换纹理

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第37课-自动切换纹理 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…

jupyter notebook使用conda环境

pycharm中安装过可以使用的库在jupyter notebook中导入不进来 1 检查pycharm中安装的库的位置 2 检查jupyter notebook中安装的库的位置 3 查看jupyter notebook内核名字 可以看到jupyter notebook中内核名字叫ipykernel 4 安装ipykernel 在pycharm的terminal中 pip instal…

【C语言】动态内存经典笔试题(下卷)

前言 如果说动态内存是C语言给我们的一个工具&#xff0c;那么只有掌握了工具的特点我们才能更好地使用。 紧随上卷&#xff0c;我们再来看看动态内存另外两道经典的笔试题。 &#xff08;建议没看过上卷的朋友可以先看完上卷再回来&#xff1a;【C语言】动态内存经典笔试题…

项目总结报告(Word模板)

2 项目工作成果 2.1 交付给用户的产品 2.2 交付给研发中心的产品 2.2.1 代码部分 2.2.2 文档部分 2.3 需求完成情况与功能及性能符合性统计 2.3.1 需求完成情况统计 2.3.2 功能符合性分析 2.3.3 性能符合性分析 3 项目工作分析 3.1 项目计划与进度实施分析 3.1.1 开发进度 3.1.…

2024年6月8日 (周六) 叶子游戏新闻

万能嗅探: 实测 网页打开 某视频号、某音、某红薯、某站&#xff0c;可以做到无水印的视频和封面下载功能哦&#xff0c;具体玩法大家自行发挥吧。 《丝之歌》粉丝又要失望&#xff1a;大概率不会亮相Xbox发布会即将于后天举行的 Xbox 发布会预计将会有许多令人兴奋的消息。早些…

STM32F103C8T6 HAL库 printf重定向 USART1 DMA方式发送数据

前言&#xff1a; 在上一篇文章里&#xff0c;我采用printf重定向为usart1&#xff0c;但是这样发送&#xff0c;对于MPU的负载比较大&#xff0c;所以本篇文章采用DMA方式&#xff0c;解放MPU资源&#xff0c;去做其他的事情&#xff0c;这里仅做为自己的记录。 正文开始&…

Halcon 双相机标定与拼图(二)

一、概述 这种标定有两种模式&#xff0c;有一个标定板和多个标定板两种 一个标定板 两个相机的重叠区域比较大&#xff0c;那么我们可以把标定板放到那个重叠区域来统一坐标系&#xff0c;如下 这种是只需要一个标定板&#xff0c;这种是推荐的方式 。这种是比较简单的&…