epoll 经验

本文分享了关于epoll的一些关键点:1) 单个epoll串行处理可能导致效率问题,建议配合线程池;2) 同一fd在多个epoll中注册会同时触发;3) fd关闭时自动从epoll中移除;4) 多事件同时触发会合并返回;5) epoll_wait监听epollhup无需加入events;6) 使用ready位避免et模式下的饥饿问题,如Nginx做法;7) 减少epoll_ctl调用以降低性能开销。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、epoll相对于select的优势:
select/poll的缺点:
1.每次调用时要重复地从用户态读入参数。
2.每次调用时要重复地扫描文件描述符。
3.每次在调用开始时,要把当前进程放入各个文件描述符的等待队列。在调用结束后,又把进程从各个等待队列中删除。
4.支持的FD个数比较少,需要修改源代码重新编译内核。

epoll的优点:
1、支持的FD==进程打开的最大FD个数
2、IO效率不随FD数目增加而线性下降(FD中有大量的空闲连接),OS内核对epoll的支持、优化。
           epoll只在epoll_ctl时把current挂一遍(这第一遍是免不了的)并给每个fd一个命令“好了就调回调函数”,如果设备有事件了,通过回调函数,会把fd放入rdllist,而每次调用epoll_wait就只是收集rdllist里的fd就可以了——epoll巧妙的利用回调函数,实现了更高效的事件驱动模型。
3、mmap加速内核与用户空间的消息传递: 内核和用户空间mmap同一块内存实现的。

二、epoll的使用

LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪(参照Stevens的书)了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。如果是以LT方式处理,只要数据未处理完(send、recv内核缓冲有数据)就会不停的收到 EPOLLIN/EPOLLOU消息(已设置监听)。

steven 《套接字编程:卷1》
fd 就绪条件
读: 1、有数据,接受缓冲区中的数据量大于某个阈值;
        2、套接字半关闭,读返回0;
        3、套接字错误,读返回-1;
        4、监听套接字
写:  1、发送缓存有空间,大于某个阈值;
         2、写半部关闭,如果写将产生SIGPIPE信号;
         3、非阻塞的connection
         4、套接字错误,写返回-1
         5、带外数据
 
ET (edge-triggered) 是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。 主要依靠应用程序处理fd,epoll_wait只能得到那些由未就绪变成就绪状态的fd。一旦变成就绪,epoll将不再关注这个fd。
       如果是以ET方式处理,事件只会通知一次,如数据到达可接收,需要使用while循环来接入数据。同样可发送也只会通常一次。send/recv都以收到EAGAIN为标识,如果还没处理完,才会现收到事件通知。
      但这并不是说每次read()时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read()返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。
 
       有些博客说, epoll_wait 运行原理: 等侍注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。  并且将注册在epfd上的socket fd的事件类型给清空,所以如果下一个循环你还要关注这个socket fd的话,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置socket fd的事件类型。 这时不用EPOLL_CTL_ADD,因为socket fd并未清空,只是事件类型清空”。
       其实这是不对的,设置了EPOLLONESHOT才是这种效果。经过测试我发现没有设置EPOLLONESHOT​,监听的事件不变。

其他网上的资料:

1、单个epoll并不能解决所有问题,特别是你的每个操作都比较费时的时候,因为epoll是串行处理的。 所以你有还是必要建立线程池来发挥更大的效能。 

2、如果fd被注册到两个epoll中时,如果有时间发生则两个epoll都会触发事件。

3、如果注册到epoll中的fd被关闭,则其会自动被清除出epoll监听列表。
4、如果多个事件同时触发epoll,则多个事件会被联合在一起返回。
5、epoll_wait会一直监听epollhup事件发生,所以其不需要添加到events中。
6、为了避免大数据量io时,et模式下只处理一个fd,其他fd被饿死的情况发生。linux建议可以在fd联系到的结构中增加ready位,然后epoll_wait触发事件之后仅将其置位为ready模式,然后在下边轮询ready fd列表。这点很有意义,Nginx也是这么做的。

7、尽量少使用epoll_ctl,过多的使用epoll_ctl系统调用,有一定的性能开销,可能成为这个系统的瓶颈


参考资料:

http://blog.linezing.com/2011/01/%E5%89%96%E6%9E%90-epoll-etlt-%E8%A7%A6%E5%8F%91%E6%96%B9%E5%BC%8F%E7%9A%84%E6%80%A7%E8%83%BD%E5%B7%AE%E5%BC%82%E8%AF%AF%E8%A7%A3%EF%BC%88%E5%AE%9A%E6%80%A7%E5%88%86%E6%9E%90%EF%BC%89

、自己的使用经验

    由于项目原因,需要一个代理工具让外网用户能够远程连接局域网内部的虚拟机。之前已经写好一个基于浏览器代理vnc4server的服务器,于是想结合Nginx的源码,试验一下epoll,开发一个基于http协议shellinaboxd的代理服务器。
    代码已在githhub中: https://github.com/hpghy/websshproxy
    这里主要讲一下有关epoll的使用。
    接下来的改进是想处理惊群现象,使用单独的一个进程处理监听套机字,创建连接套机字并发送给其他的工作进程。
    待续……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值