linux epoll.docx

上传人:b****5 文档编号:2929890 上传时间:2022-11-16 格式:DOCX 页数:35 大小:133.51KB
下载 相关 举报
linux epoll.docx_第1页
第1页 / 共35页
linux epoll.docx_第2页
第2页 / 共35页
linux epoll.docx_第3页
第3页 / 共35页
linux epoll.docx_第4页
第4页 / 共35页
linux epoll.docx_第5页
第5页 / 共35页
点击查看更多>>
下载资源
资源描述

linux epoll.docx

《linux epoll.docx》由会员分享,可在线阅读,更多相关《linux epoll.docx(35页珍藏版)》请在冰豆网上搜索。

linux epoll.docx

linuxepoll

1.什么是epoll

epoll是什么?

按照man手册的说法:

是为处理大批量句柄而作了改进的poll。

当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4)isanewAPIintroducedinLinuxkernel2.5.44),它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

 

2.epoll的相关系统调用

epoll只有epoll_create,epoll_ctl,epoll_wait3个系统调用。

 

2.1.1.intepoll_create(intsize);

创建一个epoll的句柄。

自从linux2.6.8之后,size参数是被忽略的。

需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

 

2.2.2.intepoll_ctl(intepfd,intop,intfd,structepoll_event*event);

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

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

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

EPOLL_CTL_ADD:

注册新的fd到epfd中;

EPOLL_CTL_MOD:

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

EPOLL_CTL_DEL:

从epfd中删除一个fd;

 

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

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

[cpp]viewplaincopy

print?

//保存触发事件的某个文件描述符相关的数据(与具体使用方式有关)  

  

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 */  

};  

//保存触发事件的某个文件描述符相关的数据(与具体使用方式有关)

typedefunionepoll_data{

void*ptr;

intfd;

__uint32_tu32;

__uint64_tu64;

}epoll_data_t;

//感兴趣的事件和被触发的事件

structepoll_event{

__uint32_tevents;/*Epollevents*/

epoll_data_tdata;/*Userdatavariable*/

};

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

EPOLLIN :

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

EPOLLOUT:

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

EPOLLPRI:

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

EPOLLERR:

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

EPOLLHUP:

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

EPOLLET:

 将EPOLL设为边缘触发(EdgeTriggered)模式,这是相对于水平触发(LevelTriggered)来说的。

EPOLLONESHOT:

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

2.3.3.intepoll_wait(intepfd,structepoll_event*events,intmaxevents,inttimeout);

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

参数events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。

maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。

如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时。

 

3.epoll工作原理

epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。

 

另一个本质的改进在于epoll采用基于事件的就绪通知方式。

在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

 

Epoll的2种工作方式-水平触发(LT)和边缘触发(ET)

假如有这样一个例子:

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

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

3.调用epoll_wait

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

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

5.调用epoll_wait

(2)......

3.1.EdgeTriggered工作模式

如果我们在第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()返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。

3.2.LevelTriggered工作模式

相反的,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll

(2),并且无论后面的数据是否被使用,因此他们具有同样的职能。

因为即使使用ET模式的epoll,在收到多个chunk的数据的时候仍然会产生多个事件。

调用者可以设定EPOLLONESHOT标志,在epoll_wait

(2)收到事件后epoll会与事件关联的文件句柄从epoll描述符中禁止掉。

因此当EPOLLONESHOT设定后,使用带有EPOLL_CTL_MOD标志的epoll_ctl

(2)处理文件句柄就成为调用者必须作的事情。

LT(leveltriggered)是epoll缺省的工作方式,并且同时支持block和no-blocksocket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。

如果你不作任何操作,内核还是会继续通知你 的,所以,这种模式编程出错误可能性要小一点。

传统的select/poll都是这种模型的代表.

 

ET(edge-triggered)是高速工作方式,只支持no-blocksocket,它效率要比LT更高。

ET与LT的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。

而LT模式正好相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。

因此,LT模式下开发基于epoll的应用要简单些,不太容易出错。

而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。

图示说明:

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

 

4.epoll的优点:

4.1.支持一个进程打开大数目的socket描述符(FD)

   select 最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是2048。

对于那些需要支持的上万连接数目的IM服务器来说显然太少了。

这时候你一是可以选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降,二是可以选择多进程的解决方案(传统的 Apache方案),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。

不过 epoll则没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat/proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

 

4.2.2.IO效率不随FD数目增加而线性下降

    传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是"活跃"的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。

但是epoll不存在这个问题,它只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。

那么,只有"活跃"的socket才会主动的去调用 callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个"伪"AIO,因为这时候推动力在os内核。

在一些 benchmark中,如果所有的socket基本上都是活跃的---比如一个高速LAN环境,epoll并不比select/poll有什么效率,相反,如果过多使用epoll_ctl,效率相比还有稍微的下降。

但是一旦使用idleconnections模拟WAN环境,epoll的效率就远在s

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1