Epoll用法及读写触发条件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Epoll用法及读写触发条件相关的知识,希望对你有一定的参考价值。

参考技术A 一、函数解析

1. size不是最大值,而是内核如何对内部结构进行维度设置的提示。

2. epoll_create返回的文件描述符必须使用close关闭。

返回值:成功(非负文件描述符) 失败(-1)

errno(EINVAL-size非正 | ENFILE-文件描述符用完 | ENOMEM-内存不足)

events成员有:

EPOLLIN 关联的文件描述符可读

EPOLLOUT 关联的文件描述符可写

EPOLLRDHUP 流式套接字对端关闭连接或关闭写通道(ET模式写非常有用)2.6.17

EPOLLPRI 关联的文件描述符紧急数据可读

EPOLLERR 关联的文件描述符发生错误

EPOLLHUP  关联的文件描述符挂起

EPOLLET ET模式

EPOLLONESHOT 关联的文件描述符设置一次性行为 2.6.2

op操作有:

EPOLL_CTL_ADD 增加

EPOLL_CTL_MOD 修改

EPOLL_CTL_DEL 删除

返回值:成功(0) 失败(-1)

errno(EBADF-epfd或者fd不是合法的 | EEXIST-重复增加 | EINVAL-epfd不是epoll描述符或者epfd=fd | ENOENT-修改删除的fd不在epoll中 | ENOMEM-内存不足 | EPERM-fd不支持epoll)

1. timeout为0表示立马返回, 为-1表示无限等待

2. 超时或者达到maxevents都会返回

返回值:成功(就绪的文件描述符数量) 失败(-1)

errno(EBADF-epfd不合法 | EFAULT-events没有写权限 | EINTR-超时 | EINVAL-epfd不是epoll描述符或者maxevents小于0) 

二、写过程

水平触发(LT):只要写缓冲区还有空间,就返回写就绪。

边缘触发(ET):

    1.首次加入epoll且写缓冲区有空间,返回写就绪(参考四用例第一次调用printf)

    2.写缓冲区内容被取走,返回写就绪(参考四用例的fflush,\n有类似作用)

    3.EPOLL_CTL_MOD修改关联文件描述符event,且写缓冲区有空间,返回写就绪(参考四用例epoll_ctl)

三、读过程

水平触发:只要读缓冲区有数据,就返回可读。

边缘触发:

    数据到来的时候返回可读。(即如果上一次没有读完的数据,需要等到下一次数据到来的时候才能继续读)。

四、用例(写过程)

#include <stdio.h>

#include <sys/epoll.h>

#define STDOUT_FILENO 1

int main(void)

  int epfd, nfds;

  struct epoll_event ev, events[5]; //ev用于注册事件,数组用于返回要处理的事件

  epfd = epoll_create(1); //只需要监听一个描述符——标准输出

  ev.data.fd = STDOUT_FILENO;

  ev.events = EPOLLOUT | EPOLLET; //监听读状态同时设置ET模式

  epoll_ctl(epfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev); //注册epoll事件

  for (;;)

    nfds = epoll_wait(epfd, events, 5, -1);

    for (int i = 0; i < nfds; i++)

       if (events[i].data.fd == STDOUT_FILENO)

            printf( "hello world!");

//         ev.data.fd=STDOUT_FILENO;

//         ev.events=EPOLLOUT|EPOLLET;

//         epoll_ctl (epfd,EPOLL_CTL_MOD,STDOUT_FILENO,&ev); //重新MOD事件(ADD无效),返回写就绪,循环输出

//          fflush (stdout); //读取写缓冲区数据,返回写就绪,循环输出



五、如何判断客户端关闭连接

1. TCP recv返回0, 说明对方关闭

2. 注册EPOLLERR, 收到事件是关闭

3. recv/send 返回-1时, 如果错误不是EWOULDBLOCK或者EINTR, 也主动关闭连接。

触发模式和EPOLLONESHOT

触发模式和EPOLLONESHOT

1. 基本概念

水平触发: LT

缺省的工作模式,当被监控的文件描述符上有可读写的事件发生时,epoll_wait() 就会给用户通知,如果用户没有一次的将数据读完(可能是读写缓冲区太小),那么每次调用epoll_wait(),都会给用户通知。

读缓冲区有数据 - > epoll检测到了会给用户通知

​ a.用户不读数据,数据一直在缓冲区,epoll 会一直通知

​ b.用户只读了一部分数据,epoll会通知

​ c.缓冲区的数据读完了,不通知

边缘触发: ET

当被监控的文件描述符上有可读写的事件发生时,epoll_wait() 只会给用户通知一次。哪怕数据没有一次性读完,再次调用epll_wait()也不会给用户通知。只有当再有新的读写请求到来时,调用epoll_wait()才会给用户通知,但此时读到的数据将包含是上一次没读完的遗留数据,因此边缘触发模式下一定要一次性将数据读完,否则可能出现数据不同步的情况。

读缓冲区有数据 - > epoll检测到了会给用户通知

​ a.用户不读数据,数据一致在缓冲区中,epoll下次检测的时候就不通知了

​ b.用户只读了一部分数据,epoll不通知

​ c.缓冲区的数据读完了,不通知

​ 当用户只读了一部分数据后,又有新的请求数据到来,epoll检测会通知,但此时读的数据将会包含上一次没读完的遗留数据

EPOLLONESHOT:

即使可以使用 ET 模式,一个socket 上的某个事件还是可能被触发多次。这在并发程序中就会引起一个问题。比如一个线程在读取完某个 socket 上的数据后开始处理这些数据,而在数据的处理过程中该socket 上又有新数据可读(EPOLLIN 再次被触发),此时另外一个线程被唤醒来读取这些新的数据。于是就出现了两个线程同时操作一个 socket 的局面。

一个socket连接在任一时刻都只被一个线程处理,可以使用 epoll 的 EPOLLONESHOT 事件实现。对于注册了 EPOLLONESHOT 事件的文件描述符,操作系统最多触发其上注册的一个可读、可写或者异常事件,且只触发一次,除非我们使用 epoll_ctl 函数重置该文件描述符上注册的 EPOLLONESHOT 事件。这样,当一个线程在处理某个 socket 时,其他线程是不可能有机会操作该 socket 的。但反过来思考,注册了EPOLLONESHOT 事件的 socket 一旦被某个线程处理完毕, 该线程就应该立即重置这个socket 上的 EPOLLONESHOT 事件,以确保这个 socket 下一次可读时,其 EPOLLIN 事件能被触发,进而让其他工作线程有机会继续处理这个 socket。

就是在线程回调函数process()的最后重置EPOLLONESHOT

阻塞和非阻塞:

阻塞和非阻塞是文件描述符的属性,而不是系统调用的性质,可以通过fcntl()来改变文件描述符是否阻塞

阻塞IO: 当去读一个阻塞的fd后,如果fd没有数据可读,就会一直阻塞在这里;去写一个阻塞的fd,没有空间可写,也会阻塞在那里。此时CPU无法得到释放去做其他事情

非阻塞IO: 当读一个非阻塞的fd,不论有没有数据,都会立即返回。返回成功说明读写操作完成,返回失败会设置相应的errno,根据errno做相应的处理。在没有数据读写的时候,CPU可以得到释放去做其他事情

2. 为什么边缘触发必须使用非阻塞IO

​ 前面提到的,使用边缘触发如果不一次性读取一个事件上的数据,会干扰下一个事件;造成信息不同步比如接受窗口大小是5,第一次发送了12345678,则客户端只会收到12345,当服务端再次发送一个a,则客户端就会收到678\\na。

那如果加入while循环,让其可以一次性读完数据,在while里面一直读,读完就发送,那么由于是阻塞的,读到没有数据可读,recv就会一直阻塞在那里。无法处理客户端来的信息

借用一下别人对几种IO模型的触发模式的总结:

总结:

  1. 对于监听的sockfd,最好使用水平触发模式,边缘触发模式会导致高并发情况下,有的客户端会连接不上。如果非要使用边缘触发,网上有的方案是用while来循环accept()。
  2. 对于读写的connfd,水平触发模式下,阻塞和非阻塞效果都一样,不过为了防止特殊情况,还是建议设置非阻塞。【若是要while一次性读完,也不能用阻塞的吧】
  3. 对于读写的connfd,边缘触发模式下,必须使用非阻塞IO,并要一次性全部读写完数据。

参考链接:

https://blog.csdn.net/Jiangtagong/article/details/116356621

以上是关于Epoll用法及读写触发条件的主要内容,如果未能解决你的问题,请参考以下文章

(转)epoll非阻塞读写规则

触发模式和EPOLLONESHOT

20第七周-网络编程 - IO多路复用及selectpollepoll模式详解

浅谈网络I/O多路复用模型 select & poll & epoll

百度三面

epoll边沿触发漏报消息包问题