linux epoll 模型详解

Linux网络编程——epoll原理详解及epoll反应堆模型 设想一个场景:有100万用户同时与一个进程保持着TCP连接,而每一时刻只有几十个或几百个TCP连接是活跃的(接收TCP包),也就是说在每一时刻进程只需要处理这100万连接中的一小部分连接。那么,如何才能高效的处理这种场景呢?进程是否在每次询问操作系统收集有事件发生的TCP连接时,把这100万个连接告诉操作系统,然后由操作系统找出其中有事件发生的几百个连接呢?实际上,在Linux2.4版本以前,那时的select或者poll事件驱动方式是这样做的。 阅读详情

在 linux 的网络编程中,很长的时间都在使用 select 来做事件触发。在 linux 新的内核中,有了一种替换它的机制,就是 epoll 。

相比于 select , epoll 最大的好处在于它不会随着监听 fd 数目的增长而降低效率。因为在内核中的 select 实现中,它是采用轮询来处理的,轮询的 fd 数目越多,自然耗时越多。并且,在 linux/posix_types.h 头文件有这样的声明:

#define __FD_SETSIZE    1024

表示 select 最多同时监听 1024 个 fd ,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。

 

epoll 的接口非常简单,一共就三个函数:

1. int epoll_create(int size);

创建一个 epoll 的句柄, size 用来告诉内核这个监听的数目一共有多大。这个参数不同于 select() 中的第一个参数,给出最大监听的 fd+1 的值。需要注意的是,当创建好 epoll 句柄后,它就是会占用一个 fd 值,在 linux 下如果查看 /proc/ 进程 id/fd/ ,是能够看到这个 fd 的,所以在使用完 epoll 后,必须调用 close() 关闭,否则可能导致 fd被耗尽。

 

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

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

第一个 参数是 epoll_create() 的返回值,

第二个 参数表示动作,用三个宏来表示:

EPOLL_CTL_ADD :注册新的 fd 到 epfd 中;

EPOLL_CTL_MOD :修改已经注册的 fd 的监听事件;

EPOLL_CTL_DEL :从 epfd 中删除一个 fd ;

第三个 参数是需要监听的 fd ,

第四个 参数是告诉内核需要监听什么事, struct epoll_event 结构如下:

struct epoll_event {

  __uint32_t events;  /* Epoll events */

  epoll_data_t data;  /* User data variable */

};

 

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

EPOLLIN :      表示对应的文件描述符可以读(包括对端 SOCKET 正常关闭);

EPOLLOUT :     表示对应的文件描述符可以写;

EPOLLPRI :       表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

EPOLLERR :      表示对应的文件描述符发生错误;

EPOLLHUP :      表示对应的文件描述符被挂断;

EPOLLET :       将 EPOLL 设为边缘触发 (Edge Triggered) 模式,这是相对于水平触发 (Level Triggered) 来说的。

EPOLLONESHOT : 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个 socket 的话,需要再次把这个 socket 加入到 EPOLL 队列里

 

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

等待事件的产生,类似于 select() 调用。参数 events 用来从内核得到事件的集合, maxevents 告之内核这个events 有多大,这个 maxevents 的值不能大于创建 epoll_create() 时的 size ,参数 timeout 是超时时间(毫秒,0 会立即返回, -1 将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回 0 表示已超时。

 

 

从 man 手册中,得到 ET 和 LT 的具体描述如下

 

EPOLL 事件有两种模型:

Edge Triggered (ET)  边缘触发 只有数据到来,才触发,不管缓存区中是否还有数据。

Level Triggered (LT)  水平触发 只要有数据都会触发。

 

假如有这样一个例子:

1. 我们已经把一个用来从管道中读取数据的文件句柄 (RFD) 添加到 epoll 描述符

2. 这个时候从管道的另一端被写入了 2KB 的数据

3. 调用 epoll_wait(2) ,并且它会返回 RFD ,说明它已经准备好读取操作

4. 然后我们读取了 1KB 的数据

5. 调用 epoll_wait(2)......

 

Edge Triggered 工作模式:

如果我们在第 1 步将 RFD 添加到 epoll 描述符的时候使用了 EPOLLET 标志,那么在第 5 步调用 epoll_wait(2) 之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候 ET 工作模式才会汇报事件。因此在第 5 步的时候,调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。在上面的例子中,会有一个事件产生在 RFD句柄上,因为在第 2 步执行了一个写操作,然后,事件将会在第 3 步被销毁。因为第 4 步的读取操作没有读空文件输入缓冲区内的数据,因此我们在第 5 步调用 epoll_wait(2) 完成后,是否挂起是不确定的。 epoll 工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读 / 阻塞写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用 ET 模式的 epoll 接口,在后面会介绍避免可能的缺陷。

   i    基于非阻塞文件句柄

   ii   只有当 read(2) 或者 write(2) 返回 EAGAIN 时才需要挂起,等待。但这并不是说每次 read() 时都需要循环读,直到读到产生一个 EAGAIN 才认为此次事件处理完成,当 read() 返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。

 

Level Triggered 工作模式

相反的,以 LT 方式调用 epoll 接口的时候,它就相当于一个速度比较快的 poll(2) ,并且无论后面的数据是否被使用,因此他们具有同样的职能。因为即使使用 ET 模式的 epoll ,在收到多个 chunk 的数据的时候仍然会产生多个事件。调用者可以设定 EPOLLONESHOT 标志,在 epoll_wait(2) 收到事件后 epoll 会与事件关联的文件句柄从epoll 描述符中禁止掉。因此当 EPOLLONESHOT 设定后,使用带有 EPOLL_CTL_MOD 标志的 epoll_ctl(2) 处理文件句柄就成为调用者必须作的事情。

然后详细解释 ET, LT:

LT(level triggered) 是 缺省 的工作方式 ,并且同时支持 block 和 no-block socket. 在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的 fd 进行 IO 操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的 select/poll 都是这种模型的代表.

ET(edge-triggered) 是高速工作方式 ,只支持 no-block socket 。在这种模式下,当描述符从未就绪变为就绪时,内核通过 epoll 告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了 ( 比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个 EWOULDBLOCK 错误)。但是请注意,如果一直不对这个 fd 作 IO操作 ( 从而导致它再次变成未就绪 ) ,内核不会发送更多的通知 (only once), 不过在 TCP 协议中, ET 模式的加速效用仍需要更多的 benchmark 确认(这句话不理解)。

 

在许多测试中我们会看到如果没有大量的 idle -connection 或者 dead-connection , epoll 的效率并不会比select/poll 高很多,但是当我们遇到大量的 idle- connection( 例如 WAN 环境中存在大量的慢速连接 ) ,就会发现epoll 的效率大大高于 select/poll 。(未测试)

 

另外,当使用 epoll 的 ET 模型来工作时,当产生了一个 EPOLLIN 事件后,

读数据的时候需要考虑的是当 recv() 返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取:

while(rs)

{

  buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0);

  if(buflen < 0)

  {

    // 由于是非阻塞的模式 , 所以当 errno 为 EAGAIN 时 , 表示当前缓冲区已无数据可读

    // 在这里就当作是该次事件已处理处 .

    if(errno == EAGAIN)

     break;

    else

     return;

   }

   else if(buflen == 0)

   {

     // 这里表示对端的 socket 已正常关闭 .

   }

   if(buflen == sizeof(buf)

     rs = 1;   // 需要再次读取

   else

     rs = 0;

}

还有,假如发送端流量大于接收端的流量 ( 意思是 epoll 所在的程序读比转发的 socket 要快 ), 由于是非阻塞的socket, 那么 send() 函数虽然返回 , 但实际缓冲区的数据并未真正发给接收端 , 这样不断的读和发,当缓冲区满后会产生 EAGAIN 错误 ( 参考 man send), 同时 , 不理会这次请求发送的数据 . 所以 , 需要封装 socket_send() 的函数用来处理这种情况 , 该函数会尽量将数据写完再返回,返回 -1 表示出错。在 socket_send() 内部 , 当写缓冲已满(send() 返回 -1, 且 errno 为 EAGAIN), 那么会等待后再重试 . 这种方式并不很完美 , 在理论上可能会长时间的阻塞在 socket_send() 内部 , 但暂没有更好的办法 .

 

ssize_t socket_send(int sockfd, const char* buffer, size_t buflen)

{

  ssize_t tmp;

  size_t total = buflen;

  const char *p = buffer;

  while(1)

  {

    tmp = send(sockfd, p, total, 0);

    if(tmp < 0)

    {

      // 当 send 收到信号时 , 可以继续写 , 但这里返回 -1.

      if(errno == EINTR)

        return -1;

      // 当 socket 是非阻塞时 , 如返回此错误 , 表示写缓冲队列已满 ,

      // 在这里做延时后再重试 .

      if(errno == EAGAIN)

      {

        usleep(1000);

        continue;

      }

      return -1;

    }

    if((size_t)tmp == total)

      return buflen;

    total -= tmp;

    p += tmp;

  }

 

  return tmp;

}

Linux epoll模型详解及源码分析 一、epoll简介 epoll是当前在Linux下开发大规模并发网络程序的热门选择,epoll在Linux2.6内核中正式引入,和select相似,都是IO多路复用(IO multiplexing)技术。 按照man手册的说法,epoll是为处理大批量句柄而做了改进的poll。 Linux下有以下几个经典的服务器模型: 1、PPC模型和TPC模型 PPC(Process Per Co... 阅读详情

相关推荐

linux epoll模型源码分析 一 函数实现

本文基于2.6.39内核 第一部分已经大致的介绍了linux在实现EPOLL模型所用到的主要数据结构,接下来这篇就是主要分析怎样通过这些数据结构来实现EPOLL的功能。 一。模块的加载 linux把EPOLL当做一个模块,模块入口函数的代码如下:

SunpyLiu的专栏 2065

Epoll

目录Epoll - IO多路复用怎么理解创建Epoll向EPOLL对象中添加、修改或者删除感兴趣的事件收集在epoll监控的事件中已经发生的事件完整代码 Epoll - IO多路复用 怎么理解 类比快递员寄快递,传统方式是快递员挨家挨户上楼敲门现,在改为用快递柜子的方式,比如丰巢类似,现在这里有两个集合。 1.住户所有人是一个集合。 2.丰巢是一个子集,比如今天要寄快递的住户。 epoll就是管理这两个集合。 epoll_create相当于一栋楼,聘请了一个快递员,创建一栋楼 epoll_ctl往楼里搬住

方池安夏的博客 1832

Linux 下Epoll模型

一、 介绍 Epoll 是一种高效的管理socket的模型,相对于select和poll来说具有更高的效率和易用性。传统的select以及poll的效率会因为 socket数量的线形递增而导致呈二次乃至三次方的下降,而epoll的性能不会随socket数量增加而下降。标准的linux-2.4.20内核不支持epoll,需要打patch。本文主要从linux-2.4.32和linux-2.6.10

h a 6 6 6 c k 为 你解决复杂难题! 1378

epoll_ctl()函数

epoll_ctl()函数

fucheng的博客 2165

Linux Poll与Epoll模型深入解析

本文针对Linux网络编程中的事件通知机制进行了深入分析,从poll函数的使用和不足,到epoll模型的基本用法、LT模式与ET模式的区别。通过实例演示了如何利用poll函数和epoll函数检测文件描述符状态的变化,并详细解释了在不同模式下处理读写事件的策略和性能差异。

weixin_42515392的博客 484

[pytorch] MedicalNet 3D Resnet预训练模型迁移学习实战:从分割到分类的代码改造

本文详细介绍了如何利用PyTorch对MedicalNet 3D ResNet预训练模型进行迁移学习,从分割任务改造为分类任务。通过解析模型结构、调整分类头、加载预训练权重及优化训练策略,帮助开发者在医疗影像领域快速实现高效模型部署。文章还涵盖了数据预处理、增强技巧及模型轻量化等实用内容。

kmeans3miner的博客 246

Linux多路转接之epoll

本文介绍了Linux多路转接之epoll方案

写博客记录分享学习笔记 832

【华为Hilink SDK Linux系统开发】目录 开源一小步

目录 【华为Hilink开发】第一章:华为hilink平台简介及开发流程 【华为Hilink开发】第二章:华为hilink SDK网络适配、网关添加删除 【华为Hilink开发】第三章:华为hilink SDK子设备添加、删除 【华为Hilink开发】第四章:华为hilink SDK子设备控制及同步 【华为Hilink开发】第五章:华为hilink SDK适配遇到的问题 ...

开源一小步 3619

多路复用之epoll()

滴滴滴

qq_44041062的博客 458

手把手教你搞定CAC会议投稿:从LaTeX模板到PDF eXpress避坑全流程

本文详细解析CAC会议投稿全流程,重点介绍LaTeX模板调整、Turnitin查重技巧及PDF eXpress校验避坑指南。通过实战案例和代码示例,帮助研究者高效完成IEEE格式论文投稿,避免常见技术性失误,提升投稿成功率。

weixin_29969209的博客 397

美亚杯取证比赛必备:Veracrypt挂载+检材解压保姆级教程(附常见错误排查)

本文提供美亚杯数字取证竞赛的Veracrypt挂载与检材解压保姆级教程。详细解析从分卷压缩包解压(密码格式为MeiyaCup+年份)到使用Veracrypt正确挂载加密容器的全流程,并附有常见错误排查与高级应急方案,帮助选手高效突破技术门槛,顺利进入取证分析阶段。

alice7model的博客 1036

前端 详解epoll_events结构体

Epoll 作为一种 IO 复用机制多应用与高并发领域,网上有很多如何使用 epoll 的基础教程,但对于 epoll 中很重要的结构体 epoll_event 讲的都模棱两可,这篇文章将做深入解析 在解析之前,先回顾一下 epoll 的使用方法。 首先调用int epoll_create(int size);创建一个 epoll 调用int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);为 epoll 注册事件(如果是新

木牛的博客 1万+

QT 面试题汇总

一、讲述Qt信号槽机制与优势与不足 优点:①类型安全。需要关联的信号槽的签名必须是等同的。即信号的参数类型和参数个数同接受该信号的槽的参数类型和参数个数相同。若信号和槽签名不一致,编译器会报错。 ②松散耦合。信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无需知道是那个对象的那个信号槽接收它发出的信号,它只需在适当的时间发送适当的信号即可,而不需要关心是否被接受和那个对象接受了。Qt就保证了适当的槽得到了调用,即使关联的对象在运行时被删除。程序也不会奔溃。 ...

编程菜鸟 4万+

Linux epoll模型

该程序一个简单的聊天室程序,用Linux C++写的,服务器主要是用epoll模型实现,支持高并发,我测试在有10000个客户端连接服务器的时候,server处理时间不到1秒,当然客户端只是与服务器连接之后,接受服务器的欢迎消息而已,并没有做其他的通信。LT(level triggered):水平触发,缺省方式,同时支持block和no-block socket,在这种做法中,内核告诉我们一个文件描述符是否被就绪了,如果就绪了,你就可以对这个就绪的fd进行IO操作。程序共包含2个头文件和3个cpp文件。

2401_86940371的博客 950

【AI大模型:认知升级】1、全面解析AI大模型:从起源到发展脉络与应用价值

AI大模型是具备超大规模参数(千亿至万亿级)、海量训练数据(TB级)和强迁移学习能力的基础模型。其技术特征包括Transformer架构革新、大数据预训练范式及参数规模超过临界值后的能力涌现。发展历程经历了语言模型突破(2018-2020)、多模态融合(2021-2023)和架构创新(2023至今)三波浪潮,遵循数据、算力、算法协同进化规律。底层驱动因素包括能力涌现机制、算力基础设施突破和应用场景拓展,正在重构产业价值链。通过"知识星系""认知

2025博客之星Top81。专注AI工程化与架构实战。从分布式思维到模型部署,用工程化视角为你厘清AI落地的真实路径。 2292

Vue3 - 详细实现移动端H5网页调用摄像头拍照功能,微信公众号网页h5页面打开本地摄像头并点击按钮照相,vue3获取摄像头画面执行拍照保存,调整摄像头前置后置抓拍图片给用户展示或保存手机上传服务器

vue3,H5网页,手机移动端,vue3摄像头拍照,前端h5调用手机摄像头,移动端网页调用摄像头,H5实现手机拍照和选择上传,调用本地摄像头实现拍照,vue3调用摄像头拍照,手机浏览器开启摄像头并进行拍照,H5移动端网页中调用手机摄像头,H5实现web调用摄像头在线照相,vue3h5调用摄像头,微信公众号网页如何调用摄像头拍照,公众号H5页面实现打开摄像头照相,怎么生成临时图片,vue3项目调用摄像头实现拍照功能,摄像头打不开没反应,浏览器不兼容怎么办,摄像头权限不弹出,只能在本地运行,拒绝权限后怎么办,

🏆 前端领域优质创作者 1万+

linux epoll事件模型详解

linux epoll事件模型详解(转载)   (2013-07-10 14:02:13) 转载▼ 标签:  it   分类: Linux 一、介绍     epoll是Linux(内核版本2.6及以上支持)下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CP

蓝旭晨枫╮ 592

达梦数据库导入报错:[警告]服务器与文件内数据的 CASE_SENSITIVE 参数不匹配,成功终止导入, 但出现警告dimp V8,本地编码:PG_GBK, 导入文件编码:PG_UTF8

达梦数据库管理系统的最新版本是8.0版本,简称DM8。DM8采用全新的体系架构,在保证大型通用的基础上,针对可靠性、高性能、海量数据处理和安全性做了大量的研发和改进工作,极大提升了达梦数据库产品的性能、可靠性、可扩展性,能同时兼顾OLTP和OLAP请求,从根本上提升了DM8产品的品质。达梦数据库管理系统是达梦公司推出的具有完全自主知识产权的高性能数据库管理系统,简称DM。[警告]服务器与文件内数据的 CASE_SENSITIVE 参数不匹配,成功终止导入, 但出现警告。4.无视警告,输入y,回车。

十三月的博客 1万+

Linux C epoll实现简单的Reactor模型-http server demo

reactor模型:主要用epoll实现了reactor模型,并简单的实现了http server的demo。 主要知识点:socket编程,reactor模型,I/O多路复用,epoll

C++ 开发者的博客 1543

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

linux epoll 程序实例,一个linux下的epoll事件模型实例

在red hat linux下(内核版本2.6以上)写了一个epoll事件模型的实例,读取客户端发送的socket请求,记录在日志文件中,暂时使用LT模式(水平模式)触发,当前采用阻塞方式进行,后续打算增加一个事件环状链表,采用多进程方式非阻塞得将事件放入事件链表中,通过子进程来处理具体事件。#include#include#include#include#include#include#incl...

weixin_30909873的博客 296

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Linux网络编程之epoll模型

I/O 多路复用使得程序能同时监听多个文件描述符,能够提高程序的性能, Linux 下实现 I/O 多路复用的系统调用主要有 select、 poll 和 epoll。其中,select有最大文件描述符个数1024,虽然可以通过更改系统配置文件进行更改,但是一般不建议修改系统配置文件,因为这样会影响所有的用户。而且使用select模型需要反复从用户区转到内核区,当fd很大时开销也会很大。poll原理和select差不多,其优点是突破了1024文件描述符的限制,但是编写的代码不能移值,因此,linux上很少使

weixin_44923692的博客 668

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Linux epoll 模型详解

Linux epoll 模型详解 2019-09-22 综合select和poll的一些优缺点,Linux 从内核 2.6 版本开始引入了更高效的 epoll 模型,本节我们来详细介绍 epoll 模型。 要想使用 epoll 模型,必须先需要创建一个 epollfd,这需要使用epoll_create函数去创建: 1 2 3 #includ...

XiaoWhy的专栏 473

Linux的IO模型介绍-epoll、poll、select

Linux的IO模型介绍-epoll、poll、select同步与异步IO阻塞与非阻塞IO这俩类根本就不是一回事情Linux的IO模型1.阻塞IO非阻塞IO异步IO多路复用IO(其实这就是同步IO!!!)selectpollepollepoll的两种工作模式:电平触发与边沿触发总结 同步与异步IO 这个概念其实很简单,A让B去做一件事情,然后B先回复了一声“好的”,再转而去做这件事情,那么这就是异步。如果B先做完了这件事情,然后再回复A“我做好了”,那么这就是同步。 所以说,同步还是异步,那是针对被调用者B

weixin_44039270的博客 299

Linux select/epoll网络模型

select,epoll网络模型经常在面试中出现,epoll是对poll的优化,是linux下最优秀的网络模型 epoll优点: # 相对select,没有最大并发数限制 /proc/sys/file-max # 数据传递(用户空间跟内核空间)通过共享内存(mmap)方式 # epoll_wait 直接返回被触发的fd对应的一块buffer,不需要遍历所有的fd  

hbjae28666的专栏 636

Linux网络编程--epoll 模型原理详解以及实例

1.简介Linux I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数。Linux 2.6内核中有提高网络I/O性能的新方法,即epoll 。 epoll是什么?按照man手册的说法是为处理大批量句柄而作了改进的poll。要使用epoll只需要以下的三个系统函数调用: epoll_create(2),epoll_ctl(2),epoll_wait(2)。

志存高远 1万+

深入剖析 Linux 中的 epoll:全面掌握事件驱动模型

通过本文的介绍,您应该对epoll有了更深入的了解。epoll是一个强大且高效的事件驱动模型,适合处理高并发场景。希望这篇文章能帮助您更好地理解和使用epoll,并在您的项目中发挥其强大的功能。

极客代码 1835
上一篇: 基于的epoll模型的简单http服务器
下一篇: 游戏服务端开发浅谈(一)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值