• epoll的两种模式


    从代码开始吧:

    epoll_ctl(clifd, EPOLL_CTL_ADD, EPOLLIN | EPOLLOUT);

    epoll主循环将使用水平模式(默认,EPOLLLT)监听clifd的读写状态,在水平模式下,只要clifd的内核读缓冲区存在未读的数据,每一次的epoll_wait()返回针对clifd的epoll_event都会设置EPOLLIN;只要clifd的内核写缓冲区存在可写空间,每一次的epoll_wait()返回针对clifd的epoll_event都会设置EPOLLOUT。通常来说,读光内核缓冲区不难,写满内核缓冲区就有点扯了。通常的解决方案是:

    Don't include EPOLLOUT unless you got EAGAIN from a write attempt, and remove it when you have successfully written bytes to a socket.

    例如在一个epoll循环内,监听readfd的读事件,将从readfd中获得的数据完整写到writefd中,当write()并未完整执行时,监听writefd的写事件:

     1 while (1) 
     2 {
     3     int n, i;
     4     n = epoll_wait(epoll_fd, events, 8192, -1);
     5 
     6     for (i = 0; i < n; i++)
     7     {   
     8         int fd = events[i].data.fd;
     9 
    10         if (events[i].events & EPOLLOUT)
    11         {   
    12             // ...
    13             // 找到在NonBlockSend中的缓存buf
    14             int nwrite = write(fd, buf, len);
    15             if (nwrite == len)
    16                 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
    17             else
    18                 // 更新buf
    19         }   
    20 
    21         if (events[i].events & EPOLLIN)
    22         {   
    23             char buf[1024];
    24             int len = read(fd, buf, sizeof(buf));
    25 
    26             int nwrite = write(writefd, buf, len);
    if (len == nwrite)
                return;
    27 if (-1 == nwrite && EAGAIN != errno) 28 { 29 close(writefd); 30 } 31 else 32 { 33 nwrite = nwrite == -1 ? 0 : nwrite; 34 NonBlockSend(writefd, buf + nwrite, len - nwrite); 35 } 36 } 37 } 38 }

    其中NonBlockSend()就是将数据存入writefd对应的缓冲区中,并为writefd建立EPOLLOUT事件监听。恩,看起来没什么问题了。但是,我也是刚刚才发现,问题还是有的。想象一下,某一次epoll_wait()返回writefd写事件,针对writefd的write()调用将会成功,然而,假如同时触发的还有readfd的读事件,并且该事件先于writefd写事件处理,那么,从readfd中读到的新数据将先于已缓存的旧数据发送。。。。所以,处理readfd的读事件,应该先判断writefd是否已有缓存数据,是则直接调用NonBlockSend()。

    不断修改epoll的监听事件集合好像不太好,于是我就想能否用一下边缘模式(EPOLLET),因为在该模式下,只有当writefd从不可写变为可写时,epoll_wait()才会通知writefd的写事件,也就是说,直到你再次把缓冲区写满后,epoll_wait()的返回才有可能包含writefd。对上面简单的应用场合,使用EPOLLET是合情合理的,只需要把15至17行删掉,然后从一开始就为writefd建立EPOLLOUT事件监听,而不是放到NonBlockSend()里。

    附几篇关于epoll两种模式的介绍:

    http://stackoverflow.com/questions/12892286/epoll-with-edge-triggered-event/12897334#12897334

    http://stackoverflow.com/questions/9162712/what-is-the-purpose-of-epolls-edge-triggered-option

    http://www.ccvita.com/515.html

  • 相关阅读:
    在Selenium自动化中查找损坏的链接
    WebDriverManager
    在TestNG中重试失败的测试
    算法:汉诺塔
    Java 多线程
    spring ioc原理(看完后大家可以自己写一个spring)
    Apache与Nginx的区别
    Apache与Nginx的区分比较
    Java 设计模式
    Java 理论与实践: 正确使用 Volatile 变量
  • 原文地址:https://www.cnblogs.com/madao/p/3024538.html
Copyright © 2020-2023  润新知