I/O多路复用——epoll

epoll是Linux特有的I/O复用函数,它在实现和使用上与select、poll有很大差异。

epoll使用一组函数来完成任务,而不是单个函数。

epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,从而无需像select、poll那样每次调用都要重复传入文件描述符集或事件集。

但epoll需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表。

epoll API

epoll有epoll_create、epoll_ctl、epoll_wait三个系统调用。

1.epoll_create

epoll_create创建一个额外的文件描述符,来唯一标识内核中的这个事件表。

1)size参数现在不起作用,只是给内核一个提示,告诉它事件表需要多大。

2)该函数返回的文件描述符将用作其他所有epoll系统调用的第一个参数,以指定要访问的内核事件表。

2.epoll_ctl

epoll_ctl用来操作epoll的内核事件表。

1)fd参数是要操作的文件描述符。

2)op参数指定操作类型,操作类型有如下3种:

EPOLL_CTL_ADD 往事件表上注册fd上的事件
EPOLL_CTL_MOD 修改fd上的注册事件
EPOLL_CTL_DEL 删除fd上注册的事件

3)event参数指定事件,它是epoll_event结构指针类型。


events成员描述事件类型。epoll支持的文件类型和poll基本相同。表示epoll事件类型的宏是在poll对应的宏前加上“E”。但epoll有两个额外的事件类型——EPOLLET和EPOLLONESHOT,它们对于epoll高效运作非常关键。

data成员用于存储用户数据,其类型epoll_data_t定义如下:

epoll_data是一个联合体,其四个成员中使用最多的是fd,它指定事件所从属的目标文件描述符。

ptr成员可用来指定与fd相关的用户数据。但由于epoll_data_t是一个联合体,我们不能同时使用其ptr成员和fd成员,因此,如果要将文件描述符和用户数据关联起来,以实现快速的数据访问,只能使用其他手段,比如放弃使用epoll_data_t的fd成员,而在ptr指向的用户数据中包含fd。

epoll_ctl 成功时返回0,失败返回-1并设置errno。


3.epoll_wait

它在一段超时时间内等待一组文件描述符上的事件。

参数从后往前

1)timeout参数的含义与poll接口的timeout参数相同。

2)maxevents参数指定最多监听多少个事件。

3)epoll_wait函数如果检测到事件,就将所有就绪的事件从内核事件表(由epfd参数指定)中拷贝到它的第二个参数events指向的数组中。这个数组只用于输出epoll检测到的就绪事件,而不像select和poll的数组参数那样既用于传入用户注册的事件,又用于输出内核检测到的就绪事件。这就极大提高了应用程序索引就绪文件描述符的效率。

epoll_wait成功时返回就绪的文件描述符个数,失败时返回-1并设置errno。

LT和ET模式

epoll对文件描述符的操作有两种模式:LT(Level Trigger,水平触发)模式和ET(Edge Trigger,边缘触发)模式。

LT工作模式是默认的工作模式,这种模式下epoll相当于一个效率较高的poll。当epoll_wait检测到其上有时间发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。这样,当应用程序下一次调用eoll_wait时,epoll_wait还会再次向应用程序通告该事件,直到该事件被处理。

当往epoll内核事件表中注册一个文件描述符上的EPOLLET事件时,epoll将以ET模式来操作该文件描述符。ET模式是epoll的高效工作模式。当epoll_wait检测到其上有事件发生并将此通知应用程序后,应用程序必须立即处理该事件,因为后续的epoll_wait调用将不再向应用程序通知这一事件。可见,ET模式在很大程度上降低了同一个epoll事件被重复触发的次数,因此效率要比LT模式高。

——《Linux高性能服务器编程》

LT同时支持block和non-block socket。这种模式中,内核告诉我们一个文件描述符是否就绪了,然后我们可以对这个就绪的fd进行I/O操作。如果我们不做任何操作,内核还是会继续通知我们 。所以,这种模式编程出错的可能性要小一点,传统的select/poll都是这种模式的代表。

ET是高速的工作方式,只支持non-block socket,它的效率要比LT更高。ET与LT的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。而LT模式正好相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。 因此,LT模式下开发基于epoll的应要简单些,不太容易出错。而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。

Nginx默认使用ET模式来使用epoll。


 epoll ET模式为何fd必须要设置为非阻塞

ET(边缘触发)数据就绪只会通知一次,也就是说,如果要使用ET模式,当数据就绪时,需要一直read,直到出错或完成为止。但倘若当前fd为阻塞(默认),那么在当读完缓冲区的数据时,如果对端并没有关闭写端,那么该read函数会一直阻塞,影响其他fd以及后续逻

辑。所以把fd设置为非阻塞,当没有数据的时候,read虽然读取不到任何内容,但是肯定不会被阻塞,那么此时,说明缓冲区数据已经读取完毕,需要继续处理后续逻辑(读取其他fd或者进行wait)。


epoll的优点:

1.支持一个进程打开大数目的socket描述符(fd)。

select的缺点是一个进程打开的fd是有限制的,由FD_SETSIZE指定,默认值是2048。对于那些需要支持的上万连接数目的IM服务器来说显然太少了。要解决这个问题,我们一是选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降;二是可以选择多进程的解决方案(传统的Apache方案),不过虽然Linux上面创建进程的代价比较小,但仍是不可忽略的 ,加上进程间数据同步远不不上线程间同步高效,所以也不是一种完美的方案。不过epoll则没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,这个数目和系统内存关系很大。例如在1GB内存的机器上大约是10w左右,具体数目可以cat /proc/sys/fs/file-max查看。

2.I/O效率不随fd数目增加而下降。

传统的select/poll另一个缺点是当我们拥有一个很大的socket集合,不过由于网络延时,任何时间只有部分的socket是活跃的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的回调函数实现的。

3.使用mmap加速内核与用户空间的消息传递。

无论是select、poll还是epoll都要通过内核把fd消息通知给用户空间,epoll是通过内核与用户空间mmap同一块内存实现的。

使用epoll的tcp服务器,完成简单的HTTP消息回显

用浏览器测试:

时间: 2024-12-13 09:34:35

I/O多路复用——epoll的相关文章

I/O多路复用---epoll函数测试

参考文章来源: epoll使用详解(精髓) Epoll学习笔记 epoll是直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法. epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂. epol

IO多路复用--epoll详解

epoll 或者 kqueue 的原理是什么? [转自知乎] Epoll 引入简介 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套接字,还是管道,我们都可以把他们看作流.之后我们来讨论I/O的操作,通过read,我们可以从流中读入数据:通过write,我们可以往流写入数据.现在假定一个情形,我们需要从流中读数据,但是流中还没有数据,(典型的例子为,客户端要从socket读如数据,但是服务器还没有把数据传回来),这时候该怎么办

多路复用——epoll

1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次. 2.epoll函数 epoll操作过程需要三个接口,分别如下: #include <sys/epoll.h>int epoll_create(int size);int epoll_ctl(in

I/O多路复用——epoll函数

1 select的低效率 select/poll函数效率比较低,主要有以下两个原因: (1)调用select函数后需要对所有文件描述符进行循环查找 (2)每次调用select函数时都需要向该函数传递监视对象信息 在这两个原因中,第二个原因是主要原因:每次调用select函数时,应用程序都要将所有文件描述符传递给操作系统,这给程序带来很大的负担.在高并发的环境下,无论怎样优化应用程序的代码,都无法完成应用的服务. 所以,select与poll并不适合以Web服务器端开发为主流的现代开发环境,只在要

IO多路复用--epoll

epoll就是为了 处理大批量句柄而改进的poll,相比与select,poll最大的好处在于它不会随着坚挺fd的数目增长而效率降低.因为在内核中的select是采用轮询来处理的,轮询fd的数目越多,自然耗时越多,并且slelct的监听数目有限(虽然可以通过头文件来改变,但并不治本) 一.epoll的相关系统调用 epoll只有三个简单地接口 分别为epoll_creat,epoll_ctl,epoll_wait (1)int epoll_creat(int size) 创建一个epoll句柄,

内核源码IO多路复用EPOLL

1. 简介: 本文将介绍内核epoll实现的原理.基于kernel 2.6.32版本. 本文只描述epoll对其他fd的监听,由于epoll本身也是一种文件系统,也可以被监听,这一部分不在这里介绍. 2. 基础数据结构: epoll中主要数据结构有两个,一个是epoll_create创建的epoll_fd的结构体eventpoll,一个是事件源对应的epitem结构体. epollevent的数据结构及相应解释如下:这里注意的是eventpoll中其实有两个ready list,一个是常规的rd

IO多路复用--epoll(待学习)

华清直播:http://www.makeru.com.cn/live/5413_1937.html 深度理解select.poll和epoll Linux IO模式及 select.poll.epoll详解 原文地址:https://www.cnblogs.com/y4247464/p/12237880.html

编写案例分别使用多进程、多路复用(select、epoll)实现tcp服务

-------------------------------多进程的tcp服务器-------------------------------通过为每个客户创建一个进程的方式,能够同时为多个客户进行服务器当客户不是特别多的时候,这种方式还行,如果有几百上千个,就不可取了,因为每次创建进程等过程需要好较大的资源 python代码案例: 1 #coding=utf-8 2 3 #引用对应的包 4 from socket import * 5 6 from multiprocessing impor

IO多路复用之epoll

1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次. 2.epoll接口 epoll操作过程需要三个接口,分别如下: #include <sys/epoll.h> int epoll_create(int size); int epoll_ctl(