epoll的边缘触发(ET)和水平触发(LT)
2017-05-13 11:30
253 查看
epoll的边缘触发和水平触发:
epoll的默认模式是水平触发。先大概了解一下这两种触发模式有什么不同:
水平触发(Level Trigger,也称条件触发):只要满足条件,就触发一个事件(只要有数据还未读完,就会一直触发)
边缘触发(Edge Trigger):每当状态发生变化时就触发一个事件。
可能概念不容易理解,这里举一个例子大概就能明白两者的区别了:比如某个人让你去买几袋酱油,你只买了一袋回去,水平触发的做法就是他让你继续去把剩下的几袋酱油买回来,如果没有完成任务,就一直通知你;边缘触发的做法就是不管完没完成任务,反正他让你买了,买没买完就是你自己的事了,下次买酱油这件事他就不管了,会让你去做其它的事。
通过上面的例子,我们对边缘触发和水平触发有了一个大概的了解,下面通过代码来深入了解:
#include <stdio.h> #include <stdlib.h> #include <sys/epoll.h> #include <unistd.h> #include <string.h> #define MAXLINE 10 int main() { pid_t pid; int fd[2]; int i; char str[MAXLINE], ch = 'a'; bzero(str, sizeof(str)); //使用管道,fd[0]默认是读端,fd[1]默认是写端 pipe(fd); pid = fork(); if(pid == 0) //child 负责写端 { close(fd[0]); while(1) { for(i = 0; i < MAXLINE / 2; i++) { str[i] = ch; } ch++; str[i - 1] = '\n'; for(; i < MAXLINE; i++) { str[i] = ch; } str[i - 1] = '\n'; write(fd[1], str, sizeof(str)); sleep(5); } } else if(pid > 0) //parent 负责读端 { close(fd[1]); struct epoll_event event; struct epoll_event resevent[10]; int res; //调用epoll_create创建红黑树树根 int efd = epoll_create(10); event.data.fd = fd[0]; //触发方式默认是EPOLLLT event.events = EPOLLIN; //将读端文件描述符加入epoll监听的树中 epoll_ctl(efd, EPOLL_CTL_ADD, fd[0], &event); while(1) { //当子进程发送数据时,就会触发事件进行读事件 res = epoll_wait(efd, resevent, 10, -1); if(resevent[0].data.fd == fd[0]) { int len = read(fd[0], str, MAXLINE/2); //写回屏幕 write(STDOUT_FILENO, str, len); } } close(efd); } if(pid > 0) close(fd[0]); else close(fd[1]); return 0; }
这段程序运行的结果是:
aaaa bbbb bbbb cccc ....
当我们修改成边缘触发(这里就不贴出完整代码了,因为改动很少),只需要把
event.events = EPOLLIN;改成
event.events = EPOLLIN | EPOLLET即可。
运行的结果是:
aaaa bbbb bbbb cccc ....
虽然结果是一样的,但是可以发现每过5秒,第一段程序会输出10个字符(包括换行),而第二段程序只会输出5个。而且我们经过分析不难发现
str[MAXLINE]数组在水平触发时,每次是全部输出的,而边缘触发情况下,每次只输出了一半,这是因为我们父进程读的时候只读了一半。这就说明了,在水平触发模式下,只要有剩余的数据,
epoll_wait会一直通知你,而在边缘触发模式下,则每个文件描述符只会通知一次。
那么问题来了,很明显传过来的数据我们是需要的,为了把数据读完,就需要重复调用
read来读取,考虑这样一种情况,每次循环使用
read读取10个字节,但是我们的数据总量只有21个字节,那么经过两次读取之后,还剩1个字节,再次读取时,由于不满足就会阻塞(是否阻塞要由设备的属性和设定所定,一般来说,读字符终端、网络的socket描述符、管道等会阻塞,而读磁盘上的文件一般不会)。阻塞在这肯定会影响程序的效率的。
解决方法是,当我们使用边缘触发时,将对应的文件描述符设置为非阻塞即可。
#include <stdio.h> #include <stdlib.h> #include <sys/epoll.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #define MAXLINE 10 int main() { pid_t pid; int fd[2]; int i; char str[MAXLINE], ch = 'a'; bzero(str, sizeof(str)); //使用管道,fd[0]默认是读端,fd[1]默认是写端 pipe(fd); pid = fork(); if(pid == 0) //child 负责写端 { close(fd[0]); while(1) { for(i = 0; i < MAXLINE / 2; i++) { str[i] = ch; } ch++; str[i - 1] = '\n'; for(; i < MAXLINE; i++) { str[i] = ch; } str[i - 1] = '\n'; write(fd[1], str, sizeof(str)); sleep(5); } } else if(pid > 0) //parent 负责读端 { close(fd[1]); //设置非阻塞 int flag = fcntl(fd[0], F_GETFL); flag |= O_NONBLOCK; fcntl(fd[0], F_SETFL, flag); struct epoll_event event; struct epoll_event resevent[10]; int res; //调用epoll_create创建红黑树树根 int efd = epoll_create(10); event.data.fd = fd[0]; //触发方式默认是EPOLLLT event.events = EPOLLIN; //将读端文件描述符加入epoll监听的树中 epoll_ctl(efd, EPOLL_CTL_ADD, fd[0], &event); int len; while(1) { //当子进程发送数据时,就会触发事件进行读事件 res = epoll_wait(efd, resevent, 10, -1); if(resevent[0].data.fd == fd[0]) { //循环读取,直到读完为止 while((len = read(fd[0], str, MAXLINE/2)) > 0) { write(STDOUT_FILENO, str, len); } } } close(efd); } if(pid > 0) close(fd[0]); else close(fd[1]); return 0; }
边缘触发比水平触发更高效的原因:不会让同一个文件描述符多次被处理,比如有些文件描述符已经不需要再读写了,但是在水平触发下每次都会返回,而边缘触发只会返回一次。
最后提醒一点,如果设置边缘触发,则必须将对应的文件描述符设置为非阻塞模式并且循环读取数据。否则会导致程序的效率大大下降。
poll和epoll默认采用的都是水平触发,只是epoll可以修改成边缘触发。
相关文章推荐
- ET(边缘触发)LT(水平触发)区别
- 水平触发LT、边缘触发ET
- 彻底学会使用epoll(二)——ET和LT的触发方式
- epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
- 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
- 【网络编程】epoll边缘触发(ET模式)
- epoll 水平触发和边缘触发的区别
- 剖析 epoll ET/LT 触发方式的性能差异误解(定性分析)
- linux epoll ET和LT触发深入分析
- epoll边缘触发和水平触发
- 彻底学会使用epoll(二)——ET和LT的触发方式
- 彻底学会使用epoll(二)——ET和LT的触发方式
- 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
- 彻底学会使用epoll(二)——ET和LT的触发方式
- 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
- 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
- 异步网络IO_select_poll_epoll的区别__select和poll只有水平触发_select,FD_ZERO,FD_SET,FD_ISSET,FD_CLR_epoll的水平触发(ET)有
- epoll 水平触发和边缘触发
- 剖析 epoll ET/LT 触发方式的性能差异
- 剖析 epoll ET/LT 触发方式的性能差异误解