Chinaunix首页 | 论坛 | 博客
  • 博客访问: 678038
  • 博文数量: 108
  • 博客积分: 3236
  • 博客等级: 中校
  • 技术积分: 906
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-04 21:23
文章分类

全部博文(108)

文章存档

2011年(33)

2010年(75)

我的朋友

分类: C/C++

2011-01-17 17:43:16

在陆续写了《epoll模型编程的几个问题》《epoll模型编程的几个问题2》后,发现epoll有些不理解的地方。
今天,深入了解了epoll的事件机制,发现epoll有两种事事件触发机制。
一是边沿触发(edge triggered),一是电平触发(levle triggered)
写文一篇《epoll编程终结者》,以总结并终结之


epoll的核心就是其事件机制,所以只有很好的了解其事件机制,才能更好的使用epoll.

epoll的事件分发机制分为两种一是边沿触发(edge triggered),一是电平触发(levle triggered,所谓电平触发并不是真的要用电平来触发,而是相对于边沿触发而言的).

一是边沿触发(edge triggered),故名思意,就是在事务的两个状态交替的时候触发,对socket来讲就是的新的数据来的时候触发,如果是上一次的数据,你没有收完 则不会再次提醒。除非有新的数据到来。这就是《epoll模型编程的几个问题2》中提到的现象。

如何设置边沿触发:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:表示对应的文件描述符设置成边沿触发;


如:
struct epoll_event ev;
//设置与要处理的事件相关的文件描述符
ev.data.fd=listenfd;
//设置要处理的事件类型
ev.events=EPOLLIN|EPOLLET;
//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);


因为在边沿触发中,没有收完的数据,就会内在内核中,不会再提醒(除非有新的数据到来),这就会产生数据死角的问题。
解决此问题有两种方法:
1.采用非阻塞函数,将数据收完后,再次调用epoll_wait(),对应epoll man手册的描述为

i with non-blocking file descriptors

ii by going to wait for an event only after read(2) or write(2) return EAGAIN or EWOULDBLOCK


2.在接收数据后,不管有没有收完,都调用 epoll_ctl() with EPOLL_CTL_MOD,这样相当于重新设置事件,就可以收到数据。

见《epoll模型编程的几个问题》中的分析,虽然里面说的原理是错的,但解决方法是正确的。


二 电平触发(levle triggered)

所谓电平触发只要是事务的某一状态出现就触发,对sockets来讲就是只有内核缓冲区中有数据,就会触发。而不管这数据是你上次没收完,还是新来数据。
所以你不用采用非阻塞函数,也不用再次调用epoll_ctl() with EPOLL_CTL_MOD


三 epoll相关问题QA,参见epoll man手册


Q1 What happens if you add the same fd to an epoll_set twice?
A1 You will probably get EEXIST. However, it is possible that two threads may add the same fd twice. This is a harmless condition.

Q2 Can two epoll sets wait for the same fd? If so, are events reported to both epoll sets fds?
A2 Yes. However, it is not recommended. Yes it would be reported to both.

Q3 Is the epoll fd itself poll/epoll/selectable?
A3 Yes.

Q4 What happens if the epoll fd is put into its own fd set?
(如果将epoll fd放入到它自己的fd set中,会怎么样?)
A4 It will fail. However, you can add an epoll fd inside another epoll fd set.
(将会出错,然而你可以将一个epll fd放入到另一个epoll fd 的 fd set中)

Q5 Can I send the epoll fd over a unix-socket to another process?
(我可以通过一个unix-socket将一个epoll fd传给另一个进程吗?) A5 No.
(不能)

Q6 Will the close of an fd cause it to be removed from all epoll sets automatically?
(当我关闭fd时,fd会自动从注册的epoll sets中移除吗?) A6 Yes.
(会的)

Q7 If more than one event comes in between epoll_wait(2) calls, are they combined or reported separately?
(如果有多个事件同时到达同一个epoll_wait的调用中,它们会合并,还是分开报告?)
A7 They will be combined.
(合并成一个报告,你可以知道这次epoll_wait调用收到了几个事件,每个事件的fd)

Q8 Does an operation on an fd affect the already collected but not yet reported events?
A8 You can do two operations on an existing fd. Remove would be meaningless for this case. Modify will re-read available I/O.

Q9 Do I need to continuously read/write an fd until EAGAIN when using the EPOLLET flag ( Edge Triggered behaviour ) ?
A9 No you don't. Receiving an event from epoll_wait(2) should suggest to you that such file descriptor is ready for the requested I/O operation. You have simply to consider it ready until you will receive the next EAGAIN. When and how you will use such file descriptor is entirely up to you. Also, the condition that the read/write I/O space is exhausted can be detected by checking the amount of data read/write from/to the target file descriptor. For example, if you call read(2) by asking to read a certain amount of data and read(2) returns a lower number of bytes, you can be sure to have exhausted the read I/O space for such file descriptor. Same is valid when writing using the write(2) function.
阅读(1758) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~