epoll事件模型
事件模型
EPOLL事件有两种模型:
Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据。
Level Triggered (LT) 水平触发只要有数据都会触发。
思考如下步骤:
- 假定我们已经把一个用来从管道中读取数据的文件描述符(RFD)添加到epoll描述符。
- 管道的另一端写入了2KB的数据
- 调用epoll_wait,并且它会返回RFD,说明它已经准备好读取操作
- 读取1KB的数据
- 调用epoll_wait……
在这个过程中,有两种工作模式:
ET模式
ET模式即Edge Triggered工作模式。
如果我们在第1步将RFD添加到epoll描述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候 ET 工作模式才会汇报事件。因此在第5步的时候,调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用ET模式的epoll接口,在后面会介绍避免可能的缺陷。
1) 基于非阻塞文件句柄
2) 只有当read或者write返回EAGAIN(非阻塞读,暂时无数据)时才需要挂起、等待。但这并不是说每次read时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。
LT模式
LT模式即Level Triggered工作模式。
与ET模式不同的是,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll,无论后面的数据是否被使用。
LT(level triggered):LT是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。
ET(edge-triggered):ET是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知。请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once).
实例一:
基于管道epoll ET触发模式
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h> #define MAXLINE 10 int main(int argc, char *argv[])
{
int efd, i;
int pfd[];
pid_t pid;
char buf[MAXLINE], ch = 'a'; pipe(pfd);
pid = fork();
if (pid == ) {
close(pfd[]);
while () {
for (i = ; i < MAXLINE/; i++)
buf[i] = ch;
buf[i-] = '\n';
ch++; for (; i < MAXLINE; i++)
buf[i] = ch;
buf[i-] = '\n';
ch++; write(pfd[], buf, sizeof(buf));
sleep();
}
close(pfd[]);
} else if (pid > ) {
struct epoll_event event;
struct epoll_event resevent[];
int res, len;
close(pfd[]); efd = epoll_create();
/* event.events = EPOLLIN; */
event.events = EPOLLIN | EPOLLET; /* ET 边沿触发 ,默认是水平触发 */
event.data.fd = pfd[];
epoll_ctl(efd, EPOLL_CTL_ADD, pfd[], &event); while () {
res = epoll_wait(efd, resevent, , -);
printf("res %d\n", res);
if (resevent[].data.fd == pfd[]) {
len = read(pfd[], buf, MAXLINE/);
write(STDOUT_FILENO, buf, len);
}
}
close(pfd[]);
close(efd);
} else {
perror("fork");
exit(-);
}
return ;
}
水平触发:
运行结果:
ubuntu1604@ubuntu:~/wangqinghe/linux/20190827$ ./et
res 1
aaaa
res 1
bbbb
res 1
cccc
res 1
dddd
res 1
eeee
res 1
ffff
^C
实例二:
基于网络C/S模型的epoll ET触发模式
server
/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h> #define MAXLINE 10
#define SERV_PORT 8080 int main(void)
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int i, efd; listenfd = socket(AF_INET, SOCK_STREAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, ); struct epoll_event event;
struct epoll_event resevent[];
int res, len;
efd = epoll_create();
event.events = EPOLLIN | EPOLLET; /* ET 边沿触发 ,默认是水平触发 */ printf("Accepting connections ...\n");
cliaddr_len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port)); event.data.fd = connfd;
epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event); while () {
res = epoll_wait(efd, resevent, , -);
printf("res %d\n", res);
if (resevent[].data.fd == connfd) {
len = read(connfd, buf, MAXLINE/);
write(STDOUT_FILENO, buf, len);
}
}
return ;
}
client
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h> #define MAXLINE 10
#define SERV_PORT 8080 int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
char buf[MAXLINE];
int sockfd, i;
char ch = 'a'; sockfd = socket(AF_INET, SOCK_STREAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT); connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); while () {
for (i = ; i < MAXLINE/; i++)
buf[i] = ch;
buf[i-] = '\n';
ch++; for (; i < MAXLINE; i++)
buf[i] = ch;
buf[i-] = '\n';
ch++; write(sockfd, buf, sizeof(buf));
sleep();
}
Close(sockfd);
return ;
}
边沿触发:
运行结果:
ubuntu1604@ubuntu:~/wangqinghe/linux/20190827$ ./et
res 1
aaaa
res 1
bbbb
res 1
cccc
^C
实例三:
基于网络C/S非阻塞模型的epoll ET触发模式
server
/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h> #define MAXLINE 10
#define SERV_PORT 8080 int main(void)
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int i, efd, flag; listenfd = socket(AF_INET, SOCK_STREAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, ); struct epoll_event event;
struct epoll_event resevent[];
int res, len;
efd = epoll_create();
/* event.events = EPOLLIN; */
event.events = EPOLLIN | EPOLLET; /* ET 边沿触发 ,默认是水平触发 */ printf("Accepting connections ...\n");
cliaddr_len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port)); flag = fcntl(connfd, F_GETFL);
flag |= O_NONBLOCK;
fcntl(connfd, F_SETFL, flag);
event.data.fd = connfd;
epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event); while () {
printf("epoll_wait begin\n");
res = epoll_wait(efd, resevent, , -);
printf("epoll_wait end res %d\n", res); if (resevent[].data.fd == connfd) {
while ((len = read(connfd, buf, MAXLINE/)) > )
write(STDOUT_FILENO, buf, len);
}
}
return ;
}
client
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h> #define MAXLINE 10
#define SERV_PORT 8080 int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
char buf[MAXLINE];
int sockfd, i;
char ch = 'a'; sockfd = socket(AF_INET, SOCK_STREAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT); connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); while () {
for (i = ; i < MAXLINE/; i++)
buf[i] = ch;
buf[i-] = '\n';
ch++; for (; i < MAXLINE; i++)
buf[i] = ch;
buf[i-] = '\n';
ch++; write(sockfd, buf, sizeof(buf));
sleep();
}
Close(sockfd);
return ;
}
epoll事件模型的更多相关文章
- Nginx Epoll事件模型优劣
L30-31 Epoll 性能优势主要源于它不用遍历 假设有100万个链接 其它事件可能都需要遍历所有链接,而Epoll只要遍历活跃的链接,这样大大提升了效率
- Linux 学习笔记之 --- epoll 事件模型详解
epoll 主要采用对已就绪的 fd 进行轮询操作 一.epoll 触发方式 epoll支持 ET 和 LT 两种触发方式 ET(边缘触发):Nginx 就是采用 ET 触发方式,只支持 no-b ...
- epoll反应堆模型
================================ 下面代码实现的思想:epoll反应堆模型:( libevent 网络编程开源库 核心思想) 1. 普通多路IO转接服务器: 红黑树 ― ...
- (转)Linux下select, poll和epoll IO模型的详解
Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...
- epoll原理详解及epoll反应堆模型
本文转载自epoll原理详解及epoll反应堆模型 导语 设想一个场景:有100万用户同时与一个进程保持着TCP连接,而每一时刻只有几十个或几百个TCP连接是活跃的(接收TCP包),也就是说在每一时刻 ...
- epoll反应堆模型实现
epoll反应堆模型demo实现 在高并发TCP请求中,为了实现资源的节省,效率的提升,Epoll逐渐替代了之前的select和poll,它在用户层上规避了忙轮询这种效率不高的监听方式,epoll的时 ...
- 【repost】JavaScript 事件模型 事件处理机制
什么是事件? 事件(Event)是JavaScript应用跳动的心脏 ,也是把所有东西粘在一起的胶水.当我们与浏览器中 Web 页面进行某些类型的交互时,事件就发生了.事件可能是用户在某些内容上的点击 ...
- jQuery的事件模型
前几天自己着重读了jQuery1.11.1的源码,又结合了之前对DE事件模型的分析,最后也实现一个简陋的事件模型. jQuery的事件系统离不开jQuery的缓存系统. jQuery的第一代缓存是直接 ...
- js事件模型与自定义事件
JavaScript 一个最简单的事件模型,需要有事件绑定与触发,还有事件删除. var eventModel = { list: {}, bind: function () { var args = ...
随机推荐
- DNS 解析
DNS即为Domain Name System的缩写形式,就是所谓的域名系统,它是互联网的一项服务.它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网. 如果想访问某个网站( ...
- yum更换源配置
今天安装mysql5.7的时候出现了点问题,最后更换yum源解决了,把这个记录一下 yum源配置(阿里云源) 1) 安装wget yum install -y wget 2) 备份/etc/yum.r ...
- springboot之手动控制事务
一.事务的重要性,相信在实际开发过程中,都有很深的了解了.但是存在一个问题我们经常在开发的时候一般情况下都是用的注解的方式来进行事务的控制,说白了基于spring的7种事务控制方式来进行事务的之间的协 ...
- C# 不是序列化xml 转实体Model【原家独创】
public static T XmlConvertModel<T>(string xmlStr) where T : class, new() { T ...
- Win7下搭建Go语言开发环境
Win7下搭建Go语言开发环境 1 下载适合window版本的Go安装包,下载地址http://code.google.com/p/go/downloads/list 2 下载适合window本本的L ...
- TODO-依赖注入与控制反转
交互框架之Actor与Listener的关系 https://www.cnblogs.com/mq0036/p/7473371.html
- ssh免密登陆(简单快捷)
介绍免密登陆配合下边这张图可以了解下过程: 假设现在A要通过免密登陆B 在A上的操作: 1.终端输入ssh-keygen (后边可以指定加密算法:-t 算法,如果不指定就是默认的rsa) 原理: 首先 ...
- obj = obj || {} 分析这个代码的起到的作用
情况一: <script> function test(obj) { console.log(obj.value) } function student() { this.value = ...
- nginx 请求限制和访问控制
请求限制 限制主要有两种类型: 连接频率限制: limit_conn_module 请求频率限制: limit_req_module HTTP协议的连接与请求 HTTP协议是基于TCP的,如果要完成一 ...
- 他爬取了B站所有番剧信息,发现了这些……
本文来自「楼+ 之数据分析与挖掘实战 」第 4 期学员 -- Yueyec 的作业.他爬取了B站上所有的番剧信息,发现了很多有趣的数据- 关键信息:最高播放量 / 最强up主 / 用户追番数据 / 云 ...