EPOLLET和EPOLLONESHOT

参考资料:
Manua pages
留下的只是回忆:epoll简介及触发模式(accept、read、send)
~青萍之末~:ET和LT模式详解
tom:EPOLL LT和ET区别

基本概念

二者均用于设置epoll_event的成员events

EPOLLET

用于设置epoll为边缘触发模式

  • 边缘触发(edge-triggered) 和 条件触发(水平触发,epoll默认)(level-triggered)

    • 条件触发中,只要输入缓冲有数据会一直通知该事件;效率低于ET,但编写简单,不需要担心事件丢失的情况
    • 边缘触发中,输入缓冲收到数据只会注册一次该事件;效率高,比LT的epoll系统调用少,但需要细致处理每个请求,否则会出现丢失事件的情况
    • 边缘触发 可以分离接收和处理数据的时间点
  • LT模式下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作,而在ET(边缘触发)模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读

  • 以man手册上的例子解释,对于如下事件
    1.向epoll实例中注册读端的fd
    2.写端写入2kB的数据
    3.调用epoll_wait并返回就绪的文件描述符fd
    4.读端从fd读取1kB数据
    5.调用epoll_wait
    若采用ET模式下的epoll,在上述 step5 会阻塞,因为只有 step3 当文件描述符发生变化时,才会触发事件

EPOLLONESHOT

ET模式降低了同一个epoll事件被重复触发的次数,但一个socket上的某个事件依旧可能被多次触发。

Linux高性能服务器编程:例如一个线程在读取完某个socket上的数据后并开始处理这些数据,而在数据的处理过程中该socket又有新的数据可读(EPOLLIN再次被触发),此时另外一个线程被唤醒来读取这些新的数据。于是出现了两个线程同时操作一个socket的局面。

对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多触发其上注册的一个可读、可写、或异常事件 且只触发一次(保证同一时刻只有一个线程为其服务)
保证了同一时刻只有一个线程为其服务,避免竞争文件描述符

  • 举例说明:
    1. 对fd注册EPOLLIN | EPOLLET | EPOLLONESHOT事件
    2. fd变为可读,调用 epoll_wait 得到 EPOLLIN事件通知
    3. 由于设置了EPOLLONESHOT,将会禁用该文件描述符,即使fd还是可读的,也不会再次触发EPOLLIN事件通知
    5. 如果想要再次接受通知,需要使用 EPOLL_CTL_MOD 重新配置文件描述符

应用

应用EPOLLET标志,建议以下面的方式调用ET模式下的epoll:

  1. 采用非阻塞文件描述符
  2. 只有当read、write返回,errno设置为EAGAIN时,才挂起等待。(并不是说每次read()时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read()返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成)
// 通过fcntl设置非阻塞套接字 
int setSocketNonBlocking(int fd)
{
    int flag = fcntl(fd, F_GETFL, 0);
    if (flag == -1)
        return -1;
    flag |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flag) == -1)
        return -1;
    return 0;
}

解释

  • 为什么ET模式下需要非阻塞套接字?(以EPOLLIN事件为例)

    • 在LT模式下,每当缓冲区有数据,当调用epoll_wait时,都会触发EPOLLIN事件,由于事件触发,可以不断地调用read操作去读数据,直到缓冲区数据读完为止;
    • 但在ET模式下,如上面所述,I/O事件发生时才会通知一次,调用epoll_wait时,只触发一次EPOLLIN事件,为了将缓冲区数据都读完,需要不断地循环调用read(当然,若数据较少,可能一次read就读完了全部的数据),但当数据读完后,若文件描述符所对应是阻塞套接字,那么会阻塞在下一次循环的read操作中,处于饥饿状态
  • 非阻塞套接字调用read后,errno 为 EAGAIN含义

    • 对非阻塞套接字进行read操作,会立即返回,若没有数据可读,程序不会阻塞等待数据就绪返回,而是返回-1,并设置errno为EAGAIN,提示当前没有数据可读请稍后再试。
    • errno代码为11(EAGAIN),perror显示Resource temporarily unavailable
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值