触发模式和EPOLLONESHOT

Posted 预期

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了触发模式和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

以上是关于触发模式和EPOLLONESHOT的主要内容,如果未能解决你的问题,请参考以下文章

epolloneshot_test.cpp:(.text+0x582): undefined reference to `pthread_create‘

epoll的两种触发模式ET、LT

Doctype的作用?严格模式和混杂模式的区分,以及如何触发着2中模式?

严格模式和混杂模式

水平触发和边沿触发(IO复用)

导入 GraphQL 解析器和模式触发错误