【在Linux世界中追寻伟大的One Piece】多路转接epoll

news2024/12/23 14:24:29

目录

1 -> I/O多路转接之poll

1.1 -> poll函数接口

1.2 -> poll的优点

1.3 -> poll的缺点

1.4 -> poll示例

1.4.1 -> 使用poll监控标准输入

2 -> I/O多路转接之epoll

2.1 -> 初识epoll

2.2 -> epoll的相关系统调用

2.2.1 -> epoll_create

2.2.2 -> epoll_ctl

2.2.3 -> epoll_wait

2.3 -> epoll工作原理

2.4 -> epoll优点


1 -> I/O多路转接之poll

1.1 -> poll函数接口

#include <poll.h>

int poll(struct pollfd* fds, nfds_t nfds, int timeout);

// pollfd 结构
struct pollfd 
{
	int fd; /* file descriptor */
	short events; /* requested events */
	short revents; /* returned events */
};

参数说明:

  • fds是一个poll函数监听的结构列表。每一个元素中,包含了三部分内容:文件描述符,监听的事件集合,返回的事件集合。
  • nfds表示fds数组的长度。
  • timeout表示poll函数的超时时间,单位是毫秒(ms)。

events和revents的取值:

事件描述是否可作为输入是否可作为输出
POLLIN数据(包括普通数据和优先数据)可读
POLLRDNORM普通数据可读
POLLRDBAND优先级带数据可读(Linux不支持)
POLLPRI高优先级数据可读,比如TCP带外数据
POLLOUT数据(包括普通数据和优先数据)可写
POLLWRNORM普通数据可写
POLLWRBAND优先级带数据可写

POLLRDHUPTCP连接被对方关闭,或者对方关闭了写操作。它由GNU引入
POLLERR错误

POLLHUP挂起。比如管道的写端被关闭后,读端描述符上将收到POLLHUP事件
POLLNVAL文件描述符没有打开

返回结果:

  • 返回值小于0,表示出错。
  • 返回值等于0,表示poll函数等待超时。
  • 返回值大于0,表示poll由于监听的文件描述符就绪而返回。

1.2 -> poll的优点

不同于select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现。

  • pollfd结构包含了要监视的event和发生的event,不再使用select"参数-值"传递的方式。接口使用比select更方便。
  • poll并没有最大数量限制(但是数量过大后性能也是会下降)。

1.3 -> poll的缺点

poll中监听的文件描述符数目增多时:

  • 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。
  • 每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中。
  • 同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

1.4 -> poll示例

1.4.1 -> 使用poll监控标准输入

#include <poll.h>
#include <unistd.h>
#include <stdio.h>

int main() 
{
	struct pollfd poll_fd;

	poll_fd.fd = 0;
	poll_fd.events = POLLIN;
	for (;;) 
	{
		int ret = poll(&poll_fd, 1, 1000);
		if (ret < 0) 
		{
			perror("poll");
			continue;
		}

		if (ret == 0) 
		{
			printf("poll timeout\n");
			continue;
		}

		if (poll_fd.revents == POLLIN) 
		{
			char buf[1024] = { 0 };
			read(0, buf, sizeof(buf) - 1);

			printf("stdin:%s", buf);
		}
	}
}

2 -> I/O多路转接之epoll

2.1 -> 初识epoll

epoll是Linux内核中提供的一种高效的IO多路复用机制,它专为处理大量文件描述符而设计。相比于传统的select和poll机制,epoll在存在大量并发连接且只有少数连接活跃时,能够显著提高系统的CPU利用率。epoll的关键优势在于它在获取就绪事件时,不会遍历所有被监听的文件描述符集,而是只会遍历那些被设备IO事件异步唤醒(通过CPU中断机制)而加入就绪链表的文件描述符集。

按照man手册的说法:是为处理大批量句柄而作了改进的poll。

它是在2.5.44内核中被引进的(epoll(4)is a new API introduced in Linux kernel 2.5.44)它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

2.2 -> epoll的相关系统调用

2.2.1 -> epoll_create

int epoll_create(int size);

创建一个epoll的句柄。

  • 自从linux2.6.8之后,size参数是被忽略的。
  • 用完之后,必须调用close()关闭。

2.2.2 -> epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);

epoll的事件注册函数。

它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

  • 第一个参数是epoll_create()的返回值(epoll 的句柄)。
  • 第二个参数表示动作,用三个宏来表示。
  • 第三个参数是需要监听的fd。
  • 第四个参数是告诉内核需要监听什么事。

第二个参数的取值:

  • EPOLL_CTL_ADD:注册新的fd到epfd中。
  • EPOLL_CTL_MOD:修改已经注册的fd的监听事件。
  • EPOLL_CTL_DEL:从epfd中删除一个fd。

struct epoll_event结构如下:

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

struct epoll_event
{
	uint32_t events; //Epoll events
	epoll_data_t data; //User data variable
}_EPOLL_PACKED;

events可以是以下几个宏的集合:

  • EPOLLIN:表示对应的文件描述符可以读(包括对端SOCKET正常关闭)。
  • EPOLLOUT:表示对应的文件描述符可以写。
  • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)。
  • EPOLLERR:表示对应的文件描述符发生错误。
  • EPOLLHUP:表示对应的文件描述符被挂断。
  • EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
  • EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

2.2.3 -> epoll_wait

int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

收集在epoll监控的事件中已经发送的事件。

  • 参数events是分配好的epoll_event结构体数组。
  • epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。
  • maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size。
  • 参数timeout是超时时间(毫秒,0会立即返回,-1是永久阻塞)。
  • 如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时, 返回小于0表示函数失败。

2.3 -> epoll工作原理

当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。

struct eventpoll 
{
	/*红黑树的根节点,这颗树中存储着所有添加到 epoll 中的需要监控的事件*/
	struct rb_root rbr;

	/*双链表中则存放着将要通过 epoll_wait 返回给用户的满足条件的事件*/
	struct list_head rdlist;
};
  • 每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。
  • 这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。
  • 而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调方法。
  • 这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。
  • 在epoll中,对于每一个事件,都会建立一个epitem结构体。
struct epitem 
{
	struct rb_node rbn;//红黑树节点
	struct list_head rdllink;//双向链表节点
	struct epoll_filefd ffd; //事件句柄信息
	struct eventpoll* ep; //指向其所属的 eventpoll 对象
	struct epoll_event event; //期待发生的事件类型
}
  • 当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。
  • 如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。这个操作的时间复杂度是O(1)。

总结一下,epoll的使用过程就是三部曲:

  • 调用epoll_create创建一个epoll句柄。
  • 调用epoll_ctl,将要监控的文件描述符进行注册。
  • 调用epoll_wait,等待文件描述符就绪。

2.4 -> epoll优点

  • 接口使用方便:虽然拆分成了三个函数,但是反而使用起来更方便高效。不需要每次循环都设置关注的文件描述符,也做到了输入输出参数分离开。
  • 数据拷贝轻量:只在合适的时候调用EPOLL_CTL_ADD将文件描述符结构拷贝到内核中,这个操作并不频繁(而select/poll都是每次循环都要进行拷贝)。
  • 事件回调机制:避免使用遍历,而是使用回调函数的方式,将就绪的文件描述符结构加入到就绪队列中,epoll_wait返回直接访问就绪队列就知道哪些文件描述符就绪。这个操作时间复杂度O(1)。即使文件描述符数目很多,效率也不会受到影响。
  • 没有数量限制:文件描述符数目无上限。

感谢各位大佬支持!!!

互三啦!!!

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

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

相关文章

推动AI云产业向深向实,云·AI·算力创新发展大会即将启幕

近年来&#xff0c;以AIGC为代表的新兴技术正加速演进&#xff0c;全球站在智能化变革的起点&#xff0c;人工智能与云计算的深度融合&#xff0c;也驱动云计算进入第三次发展浪潮&#xff0c;迎来前所未有的机遇。 伴随AI的快速发展&#xff0c;2024年《政府工作报告》明确提…

5G 现网信令参数学习(3) - RrcSetup(1)

目录 1. rlc-BearerToAddModList 1.1 rlc-Config 1.1.1 ul-AM-RLC 1.1.2 dl-AM-RLC 1.2 mac-LogicalChannelConfig 2. mac-CellGroupConfig 2.1 schedulingRequestConfig 2.2 bsr-Config 2.3 tag-Config 2.4 phr-Config 2.5 skipUplinkTxDynamic 3. physicalCellG…

【MyBatis源码】深入分析TypeHandler原理和源码

文章目录 原始 JDBC 存在的问题自定义 TypeHandler 实现TypeHandler详解BaseTypeHandler类TypeReference类型参考器43个类型处理器类型注册表&#xff1a;3个注解类枚举类 原始 JDBC 存在的问题 MyBatis 之所以设计了 TypeHandler&#xff08;类型处理器&#xff09;&#xff…

运营篇|公众号小程序如何实现流量主变现

公众号小程序如何实现流量主变现&#xff1f;三步走&#xff1a; ①开通流量主&#xff1b; ②接入广告&#xff1b; ③裂变传播&#xff1b; 一、小程序开通流量主的运营要求 微信小程序开通流量主的前提条件主要包括以下几个方面&#xff1a; 日活跃用户&#xff08;DAU…

丹摩征文活动 | 轻松上手图像生成:FLUX.1遇上ComfyUI,让复杂变简单!

一、背景 FLUX.1是由黑森林实验室推出的一款前沿图像生成模型&#xff0c;针对不同用户需求&#xff0c;提供了三种不同版本。 FLUX.1-pro是专为企业设计的闭源版本&#xff0c;拥有无与伦比的性能和生成能力。其在识别提示词、提升视觉质量、细节表现及输出多样性方面都有出…

PyTorch核心概念:从梯度、计算图到连续性的全面解析(三)

文章目录 Contiguous vs Non-Contiguous TensorTensor and ViewStrides非连续数据结构&#xff1a;Transpose( )在 PyTorch 中检查Contiguous and Non-Contiguous将不连续张量&#xff08;或视图&#xff09;转换为连续张量view() 和 reshape() 之间的区别总结 参考文献 Contig…

「IDE」集成开发环境专栏目录大纲

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「IDE」集成开发环境&#x1f4da;全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定…

Kafka经典面试题

1、kafka消息发送的流程&#xff1f; producer发送过程中启动两个线程 一个main线程 一个sender线程&#xff0c;在main线程中先创建一个双端队列&#xff08;RecordAccumlator、producerbatch&#xff09;&#xff0c;main将我们需要发送的东西经过拦截器&#xff0c;序列化&a…

手把手写深度学习(29):将DDP训练代码改成DeepSpeed

手把手写深度学习(0)&#xff1a;专栏文章导航 前言&#xff1a;deepspeed已经成为了大模型时代训练模型的常规武器&#xff0c;这篇博客以一个基于DDP的 Stable Diffusion模型训练为例&#xff0c;讲解如何从将DDP训练代码改成DeepSpeed。 目录 构建optimizer 构建scheduler…

信息收集系列(二):ASN分析及域名收集

内容预览 ≧∀≦ゞ 信息收集系列&#xff08;二&#xff09;&#xff1a;ASN分析及域名收集前言一、ASN 分析1. 获取 ASN 码2. 使用 ASNMap 获取 IP 范围3. 将 IP 范围转化为 IP 列表 二、关联域名收集1. 顶级域&#xff08;TLD&#xff09;收集测试方法 2. 根域名收集常用方法…

《数学分析》中不等式及补充

说明&#xff1a;此文章用于本人复习巩固&#xff0c;如果也能帮到大家那就更加有意义了。 注&#xff1a;1&#xff09;《数学分析》中的不等式及不等式的补充

HTML之图片和超链接的学习记录

图片 在HTML中&#xff0c;我们可以使用img标签来显示一张图片。对于img标签&#xff0c;我们只需要掌握它的三个属性&#xff1a;src、alt和title。 <img src"" alt"" title"" /> src用于指定图片所在的路径&#xff0c;这个路径可以是…

unity显示获取 年月日周几【日期】

unity显示获取 年月日周几【日期】 public void ShowDate(Text txt){//txt.text DateTime now DateTime.Now; // 获取当前时间int year now.Year; // 获取年份int month now.Month; // 获取月份&#xff08;1-12&#xff09;int day now.Day; // 获取天数&#xff08;1-31&…

【区块链】深入理解智能合约 ABI

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 深入理解智能合约 ABI&#xff08;应用程序二进制接口&#xff09;一、ABI 基础…

鸿蒙ZRouter动态路由框架—生命周期管理能力

文章目录 基本使用(单个页面生命周期&#xff09;页面的全局生命周期监听工作流程图源码 ZRouter从1.1.0版本开始支持生命周期函数管理能力&#xff0c;主要有以下特点&#xff1a; 不影响你原有的生命周期业务逻辑&#xff0c;对NavDestination页面保持着零侵入性&#xff0c;…

代码随想录算法训练营第十九天|理论基础、77. 组合、216.组合总和III、17.电话号码的字母组合

理论基础 文章链接&#xff1a;代码随想录 视频讲解&#xff1a;带你学透回溯算法&#xff08;理论篇&#xff09;| 回溯法精讲&#xff01;_哔哩哔哩_bilibili关于回溯算法&#xff0c;我公众号里已经讲完了&#xff0c;并且将回溯算法专题整理成一本PDF&#xff0c;该PDF共…

uniapp的基本使用(easycom规范和条件编译)和uview组件的安装和使用

文章目录 1、uniapp1.uview组件安装2.uview-plus组件安装 2、条件编译3、easycom规范1.组件路径符合规范2.自定义easycom配置的示例 总结 1、uniapp UniApp的UI组件库&#xff0c;如TMUI、uViewUI、FirstUI、TuniaoUI、ThorUI等&#xff0c;这些组件库适用于Vue3和TypeScript&…

深入探索GDB调试技巧及其底层实现原理

本文分为两个大模块&#xff0c;第一部分记录下本人常用到的GDB的调试方法和技巧&#xff0c;第二部分则尝试分析GDB调试的底层原理。 一、GDB调试 要让程序能被调试&#xff0c;首先得编译成debug版本&#xff0c;当然release版本的也能通过导入符号表来实现调试&#xff0c…

Kubernetes的基本概念

Kubernetes是谷歌以Borg为前身,基于谷歌15年生产环境经验的基础上开源的一个项目,Kubernetes致力于提供跨主机集群的自动部署、扩展、高可用以及运行应用程序容器的平台。 一、资源对象概述 Kubernetes中的基本概念和术语大多是围绕资源对象(Resource Object)来说的,而资…

JavaWeb后端开发案例——苍穹外卖day01

day1遇到问题&#xff1a; 1.前端界面打不开&#xff0c;把nginx.conf文件中localhost:80改成81即可 2.前后端联调时&#xff0c;前端登录没反应&#xff0c;application.yml中默认用的8080端口被占用&#xff0c;就改用了8081端口&#xff0c;修改的时候需要改两个地方&…