`
nigelzeng
  • 浏览: 606559 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

epoll介绍【转】

阅读更多

epoll是多路复用IO(I/O Multiplexing)中的一种方式,但是仅用于linux2.6以上内核,在开始讨论这个问题之前,先来解释一下为什么需要多路复用IO.

以一个生活中的例子来解释.
假设你在大学中读书,要等待一个朋友来访,而这个朋友只知道你在A号楼,但是不知道你具体住在哪里,于是你们约好了在A号楼门口见面.
如果你使用的阻塞IO模型来处理这个问题,那么你就只能一直守候在A号楼门口等待朋友的到来,在这段时间里你不能做别的事情,不难知道,这种方式的效率是低下的.
现在时代变化了,开始使用多路复用IO模型来处理这个问题.你告诉你的朋友来了A号楼找楼管大妈,让她告诉你该怎么走.这里的楼管大妈扮演的就是多路复用IO的角色.
进一步解释select和epoll模型的差异.
select版大妈做的是如下的事情:比如同学甲的朋友来了,select版大妈比较笨,她带着朋友挨个房间进行查询谁是同学甲,你等的朋友来了,于是在实际的代码中,select版大妈做的是以下的事情:

int n = select(&readset,NULL,NULL,100);
for (int i = 0; n > 0; ++i)
{
   if (FD_ISSET(fdarray[i], &readset))
   {
      do_something(fdarray[i]);
      --n;
   }
}
 
epoll版大妈就比较先进了,她记下了同学甲的信息,比如说他的房间号,那么等同学甲的朋友到来时,只需要告诉该朋友同学甲在哪个房间即可,不用自己亲自带着人满大楼的找人了.于是epoll版大妈做的事情可以用如下的代码表示:

在epoll中,关键的数据结构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_data是一个union结构体,它就是epoll版大妈用于保存同学信息的结构体,它可以保存很多类型的信息:fd,指针,等等.有了这个结构体,epoll大妈可以不用吹灰之力就可以定位到同学甲.
别小看了这些效率的提高,在一个大规模并发的服务器中,轮询IO是最耗时间的操作之一.再回到那个例子中,如果每到来一个朋友楼管大妈都要全楼的查询同学,那么处理的效率必然就低下了,过不久楼底就有不少的人了.
对比最早给出的阻塞IO的处理模型, 可以看到采用了多路复用IO之后, 程序可以自由的进行自己除了IO操作之外的工作, 只有到IO状态发生变化的时候由多路复用IO进行通知, 然后再采取相应的操作, 而不用一直阻塞等待IO状态发生变化了.
从上面的分析也可以看出,epoll比select的提高实际上是一个用空间换时间思想的具体应用.


我们目前的网络模型大都是epoll的,因为epoll模型会比select模型性能高很多, 尤其在大连接数的情况下,作为后台开发人员需要理解其中的原因。
select/epoll的特点
select的特点:select 选择句柄的时候,是遍历所有句柄,也就是说句柄有事件响应时,select需要遍历所有句柄才能获取到哪些句柄有事件通知,因此效率是非常低。但是如果连接很少的情况下, select和epoll的LT触发模式相比, 性能上差别不大。
这里要多说一句,select支持的句柄数是有限制的, 同时只支持1024个,这个是句柄集合限制的,如果超过这个限制,很可能导致溢出,而且非常不容易发现问题, TAF就出现过这个问题, 调试了n天,才发现:)当然可以通过修改linux的socket内核调整这个参数。
epoll的特点:epoll对于句柄事件的选择不是遍历的,是事件响应的,就是句柄上事件来就马上选择出来,不需要遍历整个句柄链表,因此效率非常高,内核将句柄用红黑树保存的。
对于epoll而言还有ET和LT的区别,LT表示水平触发,ET表示边缘触发,两者在性能以及代码实现上差别也是非常大的。
epoll的LT和ET的区别
LT:水平触发,效率会低于ET触发,尤其在大并发,大流量的情况下。但是LT对代码编写要求比较低,不容易出现问题。LT模式服务编写上的表现是:只要有数据没有被获取,内核就不断通知你,因此不用担心事件丢失的情况。
ET:边缘触发,效率非常高,在并发,大流量的情况下,会比LT少很多epoll的系统调用,因此效率高。但是对编程要求高,需要细致的处理每个请求,否则容易发生丢失事件的情况。
下面举一个列子来说明LT和ET的区别(都是非阻塞模式,阻塞就不说了,效率太低):
采用LT模式下, 如果accept调用有返回就可以马上建立当前这个连接了,再epoll_wait等待下次通知,和select一样。
但是对于ET而言,如果accpet调用有返回,除了建立当前这个连接外,不能马上就epoll_wait还需要继续循环accpet,直到返回-1,且errno==EAGAIN,TAF里面的示例代码:
if(ev.events & EPOLLIN)
{
    do
    {
        struct sockaddr_in stSockAddr;
        socklen_t iSockAddrSize = sizeof(sockaddr_in);
        TC_Socket cs;
        cs.setOwner(false);
        //接收连接
        TC_Socket s;
        s.init(fd, false, AF_INET);
        int iRetCode = s.accept(cs, (struct sockaddr *) &stSockAddr, iSockAddrSize);
        if (iRetCode > 0)
        {
            ...建立连接
        }
        else
        {
            //直到发生EAGAIN才不继续accept
            if(errno == EAGAIN)
            {
                break;
            }
        }
    }while(true);
}
 
同样,recv/send等函数, 都需要到errno==EAGAIN
从本质上讲:与LT相比,ET模型是通过减少系统调用来达到提高并行效率的。
epoll ET详解
ET模型的逻辑:内核的读buffer有内核态主动变化时,内核会通知你, 无需再去mod。写事件是给用户使用的,最开始add之后,内核都不会通知你了,你可以强制写数据(直到EAGAIN或者实际字节数小于 需要写的字节数),当然你可以主动mod OUT,此时如果句柄可以写了(send buffer有空间),内核就通知你。
这里内核态主动的意思是:内核从网络接收了数据放入了读buffer(会通知用户IN事件,即用户可以recv数据)
并且这种通知只会通知一次,如果这次处理(recv)没有到刚才说的两种情况(EAGIN或者实际字节数小于 需要读写的字节数),则该事件会被丢弃,直到下次buffer发生变化。
与LT的差别就在这里体现,LT在这种情况下,事件不会丢弃,而是只要读buffer里面有数据可以让用户读,则不断的通知你。
另外对于ET而言,当然也不一定非send/recv到前面所述的结束条件才结束,用户可以自己随时控制,即用户可以在自己认为合适的时候去设置IN和OUT事件:
1 如果用户主动epoll_mod OUT事件,此时只要该句柄可以发送数据(发送buffer不满),则epoll
_wait就会响应(有时候采用该机制通知epoll_wai醒过来)。
2 如果用户主动epoll_mod IN事件,只要该句柄还有数据可以读,则epoll_wait会响应。
这种逻辑在普通的服务里面都不需要,可能在某些特殊的情况需要。 但是请注意,如果每次调用的时候都去epoll mod将显著降低效率,已经吃过几次亏了!
因此采用et写服务框架的时候,最简单的处理就是:
建立连接的时候epoll_add IN和OUT事件, 后面就不需要管了
每次read/write的时候,到两种情况下结束:
1 发生EAGAIN
2 read/write的实际字节数小于 需要读写的字节数
对于第二点需要注意两点:
A:如果是UDP服务,处理就不完全是这样,必须要recv到发生EAGAIN为止,否则就丢失事件了
因为UDP和TCP不同,是有边界的,每次接收一定是一个完整的UDP包,当然recv的buffer需要至少大于一个UDP包的大小
随便再说一下,一个UDP包到底应该多大?
对于internet,由于MTU的限制,UDP包的大小不要超过576个字节,否则容易被分包,对于公司的IDC环境,建议不要超过1472,否则也比较容易分包。
B 如果发送方发送完数据以后,就close连接,这个时候如果recv到数据是实际字节数小于读写字节数,根据开始所述就认为到EAGIN了从而直接返回,等待下一次事件,这样是有问题的,close事件丢失了!
因此如果依赖这种关闭逻辑的服务,必须接收数据到EAGIN为止,例如lb。

本文收集自:
Vimer的程序世界
分享到:
评论

相关推荐

    自己封装的epoll事件驱动模型

    **epoll介绍** `epoll`是Linux内核提供的一种高效、高性能的I/O事件通知机制,主要用于解决多路复用技术(如select、poll)在高并发场景下的性能问题。epoll相比于传统的I/O多路复用技术,具有更好的扩展性和效率,...

    linux下的高并发处理select 和epoll

    本文将详细介绍这两种技术,以及它们在处理大量并发连接时的特点和优势。 首先,我们来看看`select`函数。`select`是一种古老的I/O多路复用机制,它允许程序在一个集合中的多个文件描述符上进行轮询,以检测哪些...

    Linux网络编程socket编程学习

    自学Linux网络编程关于socket...重点介绍多路I/O转接服务器的实现,包括select函数poll函数epoll函数;最后介绍了UDP协议的服务器编写和本地套接字的程序实现。资源里面有笔记,C语言代码,参考书及参考书包含的程序。

    android input system简介(android6.0)

    本文将详细介绍 IMS 的工作原理及其与其他组件的交互方式。 #### InputManagerService 的启动与初始化 1. **启动过程**: - 在 `Zygote` 进程中 `forkSystemServer` 方法被调用后,开始执行 `SystemServer` 的主...

    VC编程技巧280例

    1. **C++基础知识**:书中可能会介绍C++的基础语法,如类、对象、继承、多态等面向对象编程概念。同时,可能还会讲解指针、数组、函数、运算符重载等核心概念。 2. **MFC框架**:VC++中的MFC(Microsoft Foundation...

    windows移植linux文档

    "Socket移植.docx"可能详细介绍了这些差异和解决策略。 6. **移植过程**:"怎样将VC++开发程序从windows移植到Linux系统-厂商在线-软件直销网-软件资讯.mht"这篇文档很可能是移植的步骤指南,包括源码分析、依赖项...

    C语言后台代码

    6. **Readme**:Readme文件通常包含了项目的介绍、使用方法、注意事项等信息,是理解和使用项目的关键。在下载后,应首先阅读Readme以了解如何正确编译、运行和使用提供的代码。 通过学习和理解这些核心知识点,...

    第四章套接字通信博客的有图案的版本

    我们将详细介绍套接字通信的概念、网络协议、字节序、IP地址转换、sockaddr数据结构、套接字函数、TCP通信流程、扩展阅读、套接字初始化、套接字通信函数、三次握手、四次挥手、流量控制、TCP状态转换、半关闭、端口...

    BIND9管理员参考手册-9.18.0

    在本文档中,我们将详细介绍 BIND 9 的基本概念、安装和配置、管理和维护、故障排除等方面的知识点。 第 1 章 介绍 BIND 9 是一个开源的 DNS 服务器软件,由 Internet Systems Consortium développé。它是 BIND ...

    详细介绍IO管理器的一篇报告

    4. **多路复用IO**(如 select、poll、epoll 等):允许多个描述符在同一时间被监视,当有数据准备好时,系统通知应用程序。 四、IO管理器在不同操作系统中的实现 1. **Windows**:Windows的IO管理器主要基于完成...

    基于Linux内核framebuffer的图像采集与显示系统设计.pdf

    首先,文章详细介绍了如何在Linux内核中移植framebuffer驱动。framebuffer是Linux提供的一种基本的图形设备接口,它允许应用程序直接访问显示器的帧缓冲区,实现对图像数据的直接操作。通过移植framebuffer驱动,...

    IO模型介绍

    3. **IO多路复用**:利用内核提供的多路复用机制(如`select`、`poll`、`epoll`等),可以同时监听多个文件描述符的读写事件。当某个文件描述符准备就绪时,就会触发相应的事件,从而避免了阻塞。这种方法适合处理...

    UNIX网络编程 卷1:套接字联网API(code)

    6. **多路复用I/O**:讨论了select(), poll()和epoll()等多路复用I/O机制,这些机制允许程序同时处理多个套接字事件。 7. **并发服务器设计**:讲解了各种并发服务器模型,如单线程、多线程和异步非阻塞I/O模型,...

    C++教程网《Linux网络编程》视频百度云地址

    epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 ...

    2018年C++教程网的linux网络编程视频共41集百度云下载链接.rar

    epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 UNIX域地址...

    Linux网络编程 视频 教程

    epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 ...

    UNIX网络编程卷1和2

    3. **服务器设计模式**:介绍了单线程服务器、多线程服务器和异步非阻塞I/O模型,如select、poll和epoll等。 4. **TCP连接管理**:详细讨论了TCP的三次握手和四次挥手过程,以及TCP的各种状态转换。 5. **套接字...

    C++ SOCKET编程介绍

    C++网络编程卷一卷二中的内容可能涵盖了这些基础,包括更高级的主题,如多线程处理多个客户端连接、异步I/O模型(如select、poll、epoll)、套接字选项的设置、错误处理和调试技巧等。这些内容对于深化对C++ SOCKET...

    c++教程网的linux网络编程视频下载

    epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 ...

    [免费]2018年C++教程网的linux网络编程视频百度云下载链接.rar

    epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 ...

Global site tag (gtag.js) - Google Analytics