linux网络编程epoll详解

news2024/11/17 14:43:50

目录

  • epoll原理解析
  • epoll提供的接口
  • epoll的触发模式

epoll原理解析

从socket接收网络数据说起:
1、网络传输中,网卡会把接收到的数据写入内存,网卡向 CPU 发出一个中断信号,操作系统便能得知有新数据到来,再通过网卡中断程序去处理数据。
2、进程执行socket()函数创建socket,这个socket 对象包含了发送缓冲区、接收缓冲区与等待队列等成员,等待队列指向所有需要等待该 Socket 事件的进程。
3、假设上面socket进程为A,另外内核还有进程B和C,内核会分时执行运行状态的ABC进程。
4、当程序执行到 Recv 时,操作系统会将进程 A 从工作队列移动到该 Socket 的等待队列中,A进程被阻塞,不会往下执行代码,也就不会占用CPU资源,此时内核只剩B和C进程分时执行。
5、一个socket 对应着一个端口号,而网络数据包中包含了 IP 和端口的信息,内核可以通过端口号找到对应的socket。
6、当socket 接收到数据后,操作系统将该socket 等待队列上的进程重新放回到工作队列,该进程变成运行状态,继续执行代码。同时由于 socket 的接收缓冲区已经有了数据,Recv 可以返回接收到的数据。

epoll的设计思路:
服务服务器需要管理多个客户端连接,而Recv 只能监视单个socket,epoll 的诞生就是高效地监视多个socket。
epoll是select 和poll的增强版本,epoll的改进:
1、epoll将“维护等待队列”和“阻塞进程“分离,先用 epoll_create 创建一个epoll 对象 Epfd,再通过 epoll_ctl 将需要监视的socket 添加到 Epfd 中,最后调用 epoll_wait 等待数据。
2、内核维护一个“就绪列表”Rdlist ,引用收到数据的 Socket,当进程被唤醒后,只要获取 Rdlist 的内容,就能够知道哪些 Socket 收到数据。

epoll的工作流程
1、当某个进程调用 epoll_create 方法时,内核会创建一个 eventpoll 对象(Epfd),eventpoll 对象是文件系统中的一员,有等待队列。Rdlist 是eventpoll的成员。
2、创建 Epoll 对象后,可以用 epoll_ctl 添加或删除所要监听的 Socket,内核会将 eventpoll 添加到这个 Socket 的等待队列中。当 Socket 收到数据后,中断程序会操作 eventpoll 对象,而不是直接操作进程。
3、当 Socket 收到数据后,中断程序会给 eventpoll 的就绪列表Rdlist 添加这个Socket 引用。eventpoll 对象相当于 Socket 和进程之间的中介,Socket 的数据接收并不直接影响进程,而是通过改变 eventpoll 的就绪列表来改变进程状态。当程序执行到 epoll_wait 时,如果 Rdlist 已经引用了 Socket,那么 epoll_wait 直接返回,如果 Rdlist 为空,阻塞进程。
4、假设计算机正在运行进程 A 和进程 B,在某时刻进程 A 运行到了 epoll_wait 语句。 内核会将进程 A 放入 eventpoll 的等待队列中,阻塞进程。当 Socket 接收到数据,中断程序一方面修改 Rdlist,另一方面唤醒 eventpoll 等待队列中的进程,进程 A 再次进入运行状态。因为 Rdlist 的存在,进程 A 可以知道哪些 Socket 发生了变化。

epoll数据结构
eventpoll结构体包含了 Lock、MTX、WQ(等待队列)与 Rdlist 等成员。
就绪列表Rdlist:是一种能够快速插入和删除的数据结构,Epoll 使用双向链表来实现就绪队列。
索引结构RBR:epoll使用红黑树作为索引结构来保存监听的socket列表。
在这里插入图片描述

epoll提供的接口

1、调用epoll_create建立epoll对象,创建一个eventpoll结构体,包括rbr(在内核cache里创建红黑树用于存储以后epoll_ctl传来的socket)和rdllist(用于存储准备就绪事件的向链表)。

//创建一个epoll实例(本质是红黑树),也占用个文件描述符,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
//返回值size,用来告诉内核这个监听的数目一共有多大,自从Linux 2.6.8开始,size参数被忽略,但是依然要大于0。
int epoll_create(int size);
struct eventpoll {
  ...
  /*红黑树的根节点,这棵树中存储着所有添加到epoll中的事件,
  也就是这个epoll监控的事件*/
  struct rb_root rbr;
  /*双向链表rdllist保存着将要通过epoll_wait返回给用户的、满足条件的事件*/
  struct list_head rdllist;
  ...
};

2、调用epoll_ctl向epoll对象中添加或删除socket事件,所有添加到epoll中的事件都会与设备(如网卡)驱动程序建立回调关系,向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。

/**
 * @brief 将监听的文件描述符添加到epoll对象中
 * @param epfd epoll_create的返回值,epoll对象
 * @param op   要执行的动作:EPOLL_CTL_ADD:注册新的fd到epfd中;
                           EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
                           EPOLL_CTL_DEL:从epfd中删除一个fd;

 * @param fd   要执行动作的fd
 * @param event告诉内核需要监听什么事件,epoll_event结构体:
 *     struct epoll_event {
            __uint32_t events; // Epoll events
            epoll_data_t data; // User data variable
        };
        events可以是以下几个宏的集合(常用的IN/OUT/ERR/ET):
            EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
            EPOLLOUT:表示对应的文件描述符可以写;
            EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
            EPOLLERR:表示对应的文件描述符发生错误;
            EPOLLHUP:表示对应的文件描述符被挂断;
            EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
            EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。
        epoll_data_t联合体定义如下:(注意是联合体)
            typedef union epoll_data
            {
              void *ptr;		//可以传递任意类型数据,常用来传 回调函数
              int fd;		//可以直接传递客户端的fd
              uint32_t u32;
              uint64_t u64;
            } epoll_data_t;

 * @return 返回值:成功返回0。发生错误时返回-1并设置errno
 */
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 

3、当epoll_wait调用时,观察rdllist双向链表里有没有数据。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。

/**
 * @brief           等待epoll事件从epoll实例中发生
 * @param epfd      等待的监听描述符,也就是哪个池子中的内容
 * @param events    出参,指针,指向epoll_event的数组,监听描述符中的连接描述符就绪后,将会依次将信息填入
 * @param maxevents 表示每次能处理的最大事件数,告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size
 * @param timeout   等待时间,要是有连接描述符就绪,立马返回,如果没有,timeout时间后也返回,单位是ms;(超时情况下,0会立即返回,-1将不确定,也有说法说是永久阻塞)
 * @return          成功返回为请求的I / O准备就绪的文件描述符的数目,如果在请求的超时毫秒内没有文件描述符准备就绪,则返回零。发生错误时,epoll_wait()返回-1并正确设置errno。
 */
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); 

epoll的触发模式

epoll的两种触发模式:
边沿触发vs水平触发
epoll事件有两种模型,边沿触发:edge-triggered (EPOLLET), 水平触发:level-triggered (EPOLLLT)
水平触发(level-triggered),是epoll的默认模式
socket接收缓冲区不为空 有数据可读 读事件一直触发
socket发送缓冲区不满 可以继续写入数据 写事件一直触发
边沿触发(edge-triggered)
socket的接收缓冲区状态变化时触发读事件,即空的接收缓冲区刚接收到数据时触发读事件
socket的发送缓冲区状态变化时触发写事件,即满的缓冲区刚空出空间时触发读事件
边沿触发仅触发一次,水平触发会一直触发。
开源库:libevent 采用水平触发, nginx 采用边沿触发。

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

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

相关文章

第二证券|行业重磅白皮书发布,超高清视频产业规模剑指3万亿

在5G和超高清交融开展的布景下,下流使用需求有望迸发,超高清视频工业前景可观。 超高清工业规模有望突破3万亿 据报道,12月1日,2022国际显现工业大会分论坛——新式显现超高清主题论坛在成都举行。论坛上,中国电子信息…

文本编辑器vi--常用命令查阅版(记得收藏)

一.为何要学习vi   # 所有的UNIX-like系统都会内置vi文本编辑器,其他的文本编辑器则不一定会存在;   # 很多软件的编辑接口都会主动调用vi;   # vim具有程序编辑的能力,可以主动地以字体颜色辨别语法的正确性,方…

双元科技过会:计划募资6.5亿元,比亚迪和蜂巢能源为主要客户

近日,上海证券交易所披露的信息显示,浙江双元科技股份有限公司(下称“双元科技”)获得科创板上市委会议通过(即IPO过会)。接下来,双元科技将提交注册。 据贝多财经了解,双元科技于20…

关于天干地支及其计算

以天干地支计算日期是我国悠良的传统文化,最近在看如何计算人的生辰八字,写了个程序,但是只能算年的干支,月、日的干支计算方法太复杂了,望之只能却步,还是乖乖去查万年历比较好。这里记下关于干支的一些东…

[附源码]Python计算机毕业设计Django框架的资产管理系统设计与实现

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

[附源码]Python计算机毕业设计SSM京津冀区域产学研项目管理信息系统(程序+LW)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

50、IO流

*学习的难点:要知道在什么情况,该用什么流 补:ANSI码就是gbk码 一、基本概念: 1、什么是文件: 文件是保存数据的地方 2、文件流: 文件在程序中是以流的形式来操作的 (1)流&am…

XXL-Job海量数据处理-分片任务实战

文章目录一、需求1. 场景2. 分析3. 案例二、什么是分⽚任务2.1. 分⽚路由策略2.2. 海量数据处理2.3. 分片数量2.4. 分片值颁发2.5. 案例三、解决思路3.1. 数据拆分3.2. 分片数量3.3. 分⽚⽅式3.4. 路由策略3.5. 程序实战一、需求 1. 场景 有⼀个任务需要处理100W条数据&#…

【JavaScript 逆向】极验四代无感验证码逆向分析

前言 四代无感验证码相较于滑块验证码区别就是没有底图,一键通过模式,所以不需要轨迹以及计算缺口距离,步骤更少,四代滑块可以阅读:【JavaScript 逆向】极验四代滑块验证码逆向分析 声明 本文章中所有内容仅供学习交…

C++最后一次实验及实验总结

忙活了大半个学期,终于学完了C,虽然很基础,但是至少算是写完了实验,开心~~ 实验一 实验二 实验三 实验四 实验五 实验六 题目一 一、分析下面的程序,写出其运行时的输出结果。上机运行该程序,观察运行…

[附源码]计算机毕业设计ssm新能源电动汽车充电桩服务APP

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

ROS action客户端和服务端通信(Ubuntu )

ROS action客户端和服务端通信 gcusms ROS 一般都是用 service 和 topic 进行数据之间的交互传输,因为这种通信方式无法满数据实时反馈的要求,所以采用 action 动作消息反馈通信机制(实时反馈的任务进度,并且可以随时终止运行&am…

用 AWTK 和 AWPLC 快速开发嵌入式应用程序 (6)-在线调试

AWPLC 目前还处于开发阶段的早期,写这个系列文章的目的,除了用来验证目前所做的工作外,还希望得到大家的指点和反馈。如果您有任何疑问和建议,请在评论区留言。 1. 背景 AWTK 全称 Toolkit AnyWhere,是 ZLG 开发的开源…

vuex学习记录

为什么要用vuex 由于vue本身的特点。及页面是由多个组件构成。而组件又呈现一个二叉树状态。然后父向子需要进行通信。那如果是非父子关系,应该如何传值呢? 什么是vuex 专门为vue.js应用程序开发的状态管理模式。它采用集中式存储管理数据&#xff0c…

详解 Go 语言中的 init () 函数

阅读目录Go init 函数的详细说明包初始化Go init 函数的详细说明 初始化每个包后,会自动执行 init()函数,并且执行优先级高于主函数的执行优先级。 init 函数通常用于: 变量初始化检查 / 修复状态注册器运行计算 包…

c语言零基础入门(完整版)

1软件下载 官网下载: https://sourceforge.net/projects/orwelldevcpp/ 百度网盘:https://pan.baidu.com/s/1mhHDjO8 提取密码:mken 推荐用百度网盘,官方下载太慢了 开始安装 首先双击打开刚刚下载的软件 点击0k 因为在安装过程中不能使用…

【计算机视觉】图像形成与颜色

图像形成与颜色 光照及阴影 辐射度学 颜色 颜色信息反映了入射光的能量分布与波长,可见光的波长在400nm到760nm之间。 RGB RGB分别代表三个基色(R-红色、G-绿色、B-蓝色),如(0,0,0)表示黑色、(255, 255, 255)表示白色。其中2…

TS装饰器bindThis优雅实现React类组件中this绑定

初学React类组件时,最不爽的一点应该就是 this 指向问题了吧!初识React的时候,肯定写过这样错误的demo。 import React from react; export class ReactTestClass extends React.Component {constructor(props) { super(props); this.state …

程序员如何进化成架构师?

作为程序员的上端,每一个架构师都承担着艰巨的任务:不仅仅要和产品进行周旋,还需要有十分坚实的技术作为基础打底。 那么,架构师都在考虑什么呢? 说到底还是程序架构、结构性的问题。 比如最近十分火爆的分布式系统…