Epoll水平触发(Level Triggered)工作模式和边缘触发(Edge Triggered)工作模式区别
来源:互联网 发布:期货网络销售工作 编辑:程序博客网 时间:2024/04/29 11:20
注意:此文章适合对Epoll有初步了解的同行观看,如果还没了解epoll工作模式的同行,建议看一下链接:
Epoll多路I/O复用技术
LT模式(默认方式)
LT模式即Level Triggered工作模式。 与ET模式不同的是,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll,无论后面的数据是否被使用。 LT(level triggered):LT是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。
水平触发图例:
ET模式
ET(edge-triggered):ET是高速工作方式,只支持no-block socket。 在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知。请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。
边缘触发图例:
总结:
水平触发,是相对比较安全的,因为当内核有事件被唤醒的时候,linux系统就会将被唤醒的事件拷贝到用户态,让用户对被唤醒的事件进行处理,如果该事件没有处理,那么下一次等待中,linux内核依旧会拷贝到用户态中,保证每一次事件都能抵达用户态。 边缘触发,只会在内核被唤醒事件从无到有的那一刻,才会将事件拷贝给用户态,虽然它减少了linux内核拷贝到用户态的次数,但带来的后果有可能在linux中部分事件已经被唤醒,但是没有被获取得到。
epoll 水平触发代码:
#include "../common.h"int set_NonBlock(int fd ){ int flags = fcntl(fd, F_GETFL); flags |= O_NONBLOCK; fcntl(fd , F_SETFL, flags); return 0;}int main(){ int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.s_addr = 0; int ret = bind(fd ,(struct sockaddr*)&addr, sizeof(addr)); if (ret != 0) { perror("bind"); close(fd); return ret; } ret = listen(fd, 1024); if (ret != 0) { perror("listen"); close(fd); return ret; } set_NonBlock(fd); int epfd = epoll_create(1024); struct epoll_event event; event.data.fd = fd; event.events = EPOLLIN; ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event); if (ret != 0) { perror("epoll_ctl"); close(fd); return 0; } while ( 1 ) { struct epoll_event ev[8]; ret = epoll_wait(epfd, ev, 8, 5000); if (ret != 0) { if (errno == EINTR) continue; } for (int i = 0 ; i < ret ; i++) { int newfd; if (ev[i].data.fd == fd) { //socket fd newfd = accept(ev[i].data.fd,NULL,NULL); event.data.fd = newfd; event.events = EPOLLIN; ret = epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &event); if (ret != 0) { perror("epoll_ctl"); close(newfd); close(fd); return -1; } } else { int connectfd = ev[i].data.fd; char buf[1024] = {0}; if (read( connectfd, buf,sizeof(buf)) > 0) { printf("recv buf:%s\n",buf); } else { close(connectfd); } } } } return 0;}
epoll 边缘触发代码:
#include <sys/types.h>#include <sys/socket.h>#include <sys/stat.h>#include <sys/epoll.h>#include <arpa/inet.h>#include <sys/wait.h>#include <stdlib.h>#include <fcntl.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <stdio.h>#include <signal.h>void sig_handle(int sig){ printf("recv signal :%d\n",sig);}int main(int argc, char * argv[]){ signal(SIGPIPE,sig_handle); if (argc<2) { printf("usage:%s + [count]\n",argv[0]); return 0; } unlink("dbg.txt"); int dbg = open("dbg.txt",O_CREAT|O_APPEND|O_RDWR,0666); int count = atoi(argv[1]); int fd = socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr; memset(&addr,0,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(9988); int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); if (ret ==-1) { perror("bind"); return 0; } listen(fd,250); int is_child_process = 0;//判断在哪个进程中,父进程0,子进程1 for (int i = 0 ; i < count ; i++) { pid_t pid = fork(); if (pid==0) { is_child_process = 1; break; } } struct epoll_event ev; ev.events = EPOLLIN|EPOLLET; ev.data.fd = fd; int epfd = epoll_create(1024);//建立epfd的描述符 int flags = fcntl(fd,F_GETFL); flags |= O_NONBLOCK; fcntl(fd,F_SETFL,flags); epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev); while (1) { struct epoll_event evs[10]; int process_count = epoll_wait(epfd,evs,10,5000); if (process_count == 0) continue;//如果监听的进程都没有事件产生,则再次进入循环,继续监听 for (int i = 0 ; i < process_count ;i++) { if (evs[i].data.fd == fd) { //当进程中的socket描述符是server的socket本身时候,则accept否则就直接操作 int ret = accept(evs[i].data.fd,NULL,NULL); if (ret == -1) { printf("errno:%s",strerror(errno)); //其他错误,直接exit break; } ev.data.fd = ret; epoll_ctl(epfd,EPOLL_CTL_ADD,ret,&ev); } else { //read or write char buf[1024]; int ret = read(evs[i].data.fd,buf,sizeof(buf)); if (ret == -1) { perror("read"); if (errno == EINTR) break; exit(0); } else if (ret == 0) { //normal exit close(evs[i].data.fd); break; } //printf("recv data %s from pid:%d\n",buf,getpid()); write(dbg,"1",1); } } } if (!is_child_process) { for (int i = 0 ; i < count; i ++) { wait(NULL);//等待所有的子进程退出为止 } } return 0;}
个人感觉:
其实我使用了很久ET和LT两种模式,但是呢,ET是否就会高效率过LT?这个也不好说,其实如果读者是一个有心人的话,那么你们也可以去看看libevent的开源库,你会发现,其实他们底层的epoll,也是采用LT模式而已,所以呢,他们两者的差别具体在哪里。真不好说,希望有大牛指导指导!
2 0
- Epoll水平触发(Level Triggered)工作模式和边缘触发(Edge Triggered)工作模式区别
- epoll有两种模式,Edge Triggered(简称ET)
- epoll:Edge or Level Triggered
- 边缘触发(Edge Trigger)和条件触发(Level Trigger) epoll
- EPOLL边缘触发和水平触发的区别
- epoll 水平触发和边缘触发的区别
- epoll 水平触发和边缘触发的区别
- epoll 水平触发和边缘触发的区别
- epoll 水平触发和边缘触发的区别
- epoll 水平触发和边缘触发
- epoll的水平触发和边缘触发
- epoll的水平触发和边缘触发
- epoll 水平触发 边缘触发
- 对linux 多路复用Epoll模型的水平出发模式和边缘触发模式的理解
- 水平触发和边缘触发的区别
- 水平触发和边缘触发的区别
- 水平触发和边缘触发的区别
- 水平触发和边缘触发的区别
- Recycleview浅而又浅的分析使用
- LoadRunner11压力测试时遇到问题及解决办法
- webGIS之geoserver篇
- WSTMall 电子商务系统功能新增计划
- 为什么要用 Node.js?
- Epoll水平触发(Level Triggered)工作模式和边缘触发(Edge Triggered)工作模式区别
- Git中经常需要操作到的东西
- 基于Spring和CXF的webservice开发环境搭建
- Ionic
- 如何重开固定资产会计年度
- 焦点 向上补齐
- allegro16.5—relative propagation delay检查
- ExtJs5+Spring.Net+MVC项目搭建笔记--前台ExtJS搭建(2)
- Android开源项目第一篇——个性化控件(View)篇