从代码开始吧:

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

 posted on 2013-04-17 01:31  万事屋madao  阅读(3855)  评论(0编辑  收藏  举报