epoll:EPOLLLT和EPOLLET的区别
来源:互联网 发布:医保 车祸 知乎 编辑:程序博客网 时间:2024/05/17 00:50
概念:
Level-triggered :水平触发,缺省模式
edge-triggered :边缘触发
比如redis用LT模式,nginx用ET模式
通知模式:
LT模式时,事件就绪时,假设对事件没做处理,内核会反复通知事件就绪
ET模式时,事件就绪时,假设对事件没做处理,内核不会反复通知事件就绪
事件通知的细节:
1.调用epoll_ctl,ADD或者MOD事件EPOLLIN
LT:如果此时缓存区没有可读数据,则epoll_wait不会返回EPOLLIN,如果此时缓冲区有可读数据,则epoll_wait会持续返回EPOLLIN
ET:如果此时缓存区没有可读数据,则epoll_wait不会返回EPOLLIN,如果此时缓冲区有可读数据,则epoll_wait会返回一次EPOLLIN
2.调用epoll_ctl,ADD或者MOD事件EPOLLOUT
LT:如果不调用epoll_ctl将EPOLLOUT修改为EPOLLIN,则epoll_wait会持续返回EPOLLOUT(前提条件是写缓冲区未满)
ET:epoll_wait只会返回一次EPOLLOUT
针对TCP的测试详请,都是non-blocking:
1.listenfd设置为LT
struct epoll_event ev, events[MAX_EVENTS]; int epollfd = epoll_create(10); if (-1 == epollfd) { perror("epoll_create fail"); exit(EXIT_FAILURE); } ev.events = EPOLLIN; // LT ev.data.fd = listenfd; if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev)) { perror("epoll_ctl: listenfd fail"); exit(EXIT_FAILURE); } for (;;) { int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
表现:当3次握手完成,如果不进行accept操作,那么内核会反复通知
2.listenfd设置为ET
struct epoll_event ev, events[MAX_EVENTS]; int epollfd = epoll_create(10); if (-1 == epollfd) { perror("epoll_create fail"); exit(EXIT_FAILURE); } ev.events = EPOLLIN | EPOLLET; // ET ev.data.fd = listenfd; if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev)) { perror("epoll_ctl: listenfd fail"); exit(EXIT_FAILURE); } for (;;) { int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
表现:当3次握手完成,如果不进行accept操作,那么内核只会通知一次
3.connectfd设置为LT
if (events[n].data.fd == listenfd) { int connfd = accept(listenfd, NULL, NULL); if (-1 == connfd) { perror("accept fail"); continue; } setnonblocking(connfd); struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = connfd; if (-1 == epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &ev)) { perror("epoll_ctl: sock"); continue; }
表现:
1.客户端发送了24个字节
2.服务器一次只读取8个字节
3.内核会连续通知,epoll_wait会持续返回,直到缓冲区的数据被读取完毕或者说socket不在处于readable/writable状态
4.connectfd设置为ET
if (events[n].data.fd == listenfd) { int connfd = accept(listenfd, NULL, NULL); if (-1 == connfd) { perror("accept fail"); continue; } setnonblocking(connfd); struct epoll_event ev; ev.events = EPOLLIN | EPOLLET; ev.data.fd = connfd; if (-1 == epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &ev)) { perror("epoll_ctl: sock"); continue; }
表现:
1.客户端发送了24个字节
2.服务器一次只读取8个字节
3.内核只会通知一次,剩下的数据会留在缓冲区中
在ET模式下何时会再次通知可读写事件,也是epoll_wait有返回?
只有socket从unreadable/unwritable变为readable/writable状态:
4.当客户端再次发送数据时,则内核会继续通知可读事件
5.当服务器在每次读取完数据,显式的调用epoll_ctl来注册可读事件,如果缓冲区有可读数据则内核会继续通知可读事件
下面提供简单的测试源码,需要微调才能测试完以上几种情况:
------------------------------------------------------------------------------------------------------------------------------------------------
客户端代码:client.cpp
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <sys/epoll.h> #include <fcntl.h>#include <errno.h>#include <string.h> #define MAX_EVENTS 10 int main() { // socket struct sockaddr_in servaddr; short port = 9527; int sockfd = socket(AF_INET, SOCK_STREAM, 0); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); servaddr.sin_port = htons(port); if (connect(sockfd, (sockaddr *) &servaddr, sizeof(sockaddr_in)) < 0) { perror("connect fail"); exit(EXIT_FAILURE); } const char* buf = "daiyudong"; for (;;) { int len = (int)write(sockfd, buf, strlen(buf)); if (len > 0) { printf("write len=%d\n", len); } sleep(1); }}
服务器代码:server.cpp
#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <stdlib.h>#include <unistd.h>#include <sys/epoll.h>#include <fcntl.h>#include <errno.h>#define MAX_EVENTS 10static void setnonblocking(int fd) { int flag = fcntl(fd, F_GETFL, 0); if (flag < 0) { perror("fcntl F_GETFL:"); return; } if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) { perror("fcntl F_SETFL:"); }}static int epoll_add(int efd, int sock) { struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = sock; if (-1 == epoll_ctl(efd, EPOLL_CTL_ADD, sock, &ev)) { perror("epoll_ctl: sock"); return 1; } return 0;}static void epoll_write(int efd, int sock, bool enable) { struct epoll_event ev; ev.events = EPOLLIN | (enable ? EPOLLOUT : 0); ev.data.fd = sock; epoll_ctl(efd, EPOLL_CTL_MOD, sock, &ev);}static void epoll_del(int efd, int sock) { epoll_ctl(efd, EPOLL_CTL_DEL, sock , NULL);}int main(){ // socket int listenfd; struct sockaddr_in servaddr; short port = 9527; servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(port); listenfd = socket(AF_INET, SOCK_STREAM, 0); setnonblocking(listenfd); int res = bind(listenfd, (sockaddr *)&servaddr, sizeof(sockaddr_in)); if (0 == res) printf("server bind success, 0.0.0.0:%d\n", port); else { perror("bind fail"); exit(EXIT_FAILURE); } res = listen(listenfd, 100); if (0 == res) printf("server listen success\n"); else { perror("listen fail"); exit(EXIT_FAILURE); } // epoll struct epoll_event ev, events[MAX_EVENTS]; int epollfd = epoll_create(10); if (-1 == epollfd) { perror("epoll_create fail"); exit(EXIT_FAILURE); } ev.events = EPOLLIN; // LT ev.data.fd = listenfd; if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev)) { perror("epoll_ctl: listenfd fail"); exit(EXIT_FAILURE); } for (;;) { int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (-1 == nfds) { perror("epoll_wait fail"); exit(EXIT_FAILURE); } for (int n = 0; n < nfds; ++n) { if (events[n].data.fd == listenfd) { int connfd = accept(listenfd, NULL, NULL); if (-1 == connfd) { perror("accept fail"); continue; } setnonblocking(connfd); epoll_add(epollfd, connfd); printf("connfd:%d\n", connfd); } else if (events[n].events & EPOLLIN) { char buf[2] = {0}; int fd = events[n].data.fd; size_t count = 1; int len = (int)read(fd, buf, count); if (len > 0) { printf("read len=%d buf=%s\n", len, buf); //epoll_write(epollfd, fd, false); } else if (len < 0) { switch(errno) { case EINTR: case EAGAIN: printf("try again\n"); //epoll_write(epollfd, fd, false); break; default: epoll_del(epollfd, fd); close(fd); printf("game over\n"); } } else if (len == 0) { epoll_del(epollfd, fd); close(fd); printf("game over\n"); } } else { // pass } } }}
小结:
针对listenfd,默认使用LT模式,ET模式并无实际意义,也无收益
针对connectfd,使用ET模式则需要注意到与LT模式的读写逻辑不同,比如读取数据,则需要在一个事件内读取完毕
参考man 7 epoll
http://linux.die.net/man/7/epoll
- epoll:EPOLLLT和EPOLLET的区别
- epoll 的 EPOLLET 和 EPOLLLT
- epoll 的 EPOLLET 和 EPOLLLT
- epoll的EPOLLLT模式和EPOLLET模式比较
- epoll的EPOLLLT模式和EPOLLET模式比较
- epoll模型的EPOLLLT模式和EPOLLET模式比较
- epoll模型的EPOLLLT模式和EPOLLET模式比较
- 一个关于EPOLLET和EPOLLLT的问题
- EPOLLLT和EPOLLET
- EPOLLLT——水平触发 EPOLLET——边缘触发区别
- epoll:EPOLLLT模式下的正确读写方式
- epoll:EPOLLET模式下的正确读写方式
- epoll:EPOLLLT模式下的正确读写方式
- Select和epoll的区别
- Select和epoll的区别
- Select和epoll的区别
- select和epoll的区别
- Select和epoll的区别
- EPOLL事件之EPOLLRDHUP
- Android特效专辑(一)——水波纹过渡特效(首页)
- SSO
- ecshop商品属性按照添加顺序排列
- Easyui-DataGrid 查询,类序列化(构造匿名对象)
- epoll:EPOLLLT和EPOLLET的区别
- IOS和安卓ui设计常用尺寸及基本知识
- How to configure the kernel parameter "console"
- There is no getter for property named 'xxx' in 'class java.lang.Long'
- strong/weak/assign/copy/retain
- LeetCode 117:Populating Next Right Pointers in Each Node II
- 【Golang】【 Network programming with Go】 Templates(模版)
- 第一次写就打算给博客定一个主基调吧
- ARC/非ARC的设置