EPOLL事件有两种模型:

Edge Triggered (ET) 边缘触发只有数据到来,才触发,不管缓存区中是否还有数据。
Level Triggered (LT) 水平触发只要有数据都会触发。

首先介绍一下LT工作模式:

LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.

优点:当进行socket通信的时候,保证了数据的完整输出,进行IO操作的时候,如果还有数据,就会一直的通知你。

缺点:由于只要还有数据,内核就会不停的从内核空间转到用户空间,所有占用了大量内核资源,试想一下当有大量数据到来的时候,每次读取一个字节,这样就会不停的进行切换。内核资源的浪费严重。效率来讲也是很低的。

ET:

ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知。请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once).

优点:每次内核只会通知一次,大大减少了内核资源的浪费,提高效率。

缺点:不能保证数据的完整。不能及时的取出所有的数据。

应用场景: 处理大数据。使用non-block模式的socket。

例子:

//《client》

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <stdlib.h>

#include <fcntl.h>

#define MAXLINE  10

#define SERV_PROT 8001

int main(void)

{

  struct sockaddr_in seraddr, cliaddr;

  char buf[MAXLINE];

  int connfd, i;

  char ch = 'a';

  connfd = socket(AF_INET,SOCK_STREAM,);

  bzero(&cliaddr,sizeof(cliaddr));

  cliaddr.sin_family = AF_INET;

  cliaddr.sin_port = htons(SERV_PROT);

  cliaddr.sin_addr.s_addr = htonl(INADDR_ANY);

  connect(connfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));

  ){

    ;i < MAXLINE;i++){

      buf[i] = ch;

    }

    buf[i-] = '\n';

    ch++;

    for(;i<MAXLINE;i++)

      buf[i] = ch;

    ch++;

    write(connfd, buf, sizeof(buf));

    sleep();

  }

  close(connfd);

  ;

}

以上代码由于浏览器不支持粘贴,所以全部手打,如有错误,稍微调试即可。由于代码简单就不解释代码的意思了。。。。

//《server》

#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 8000
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;
  ];
  int res, len;
  efd = epoll_create();
/* ET 边沿触发 ,默认是水平触发 */
  event.events = EPOLLIN | EPOLLET;
/* event.events = EPOLLIN; */
  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);
  ) {
    res = epoll_wait(efd, resevent, , -);
    printf("res %d\n", res);
    ].data.fd == connfd) {
    len = read(connfd, buf, MAXLINE/);
  write(STDOUT_FILENO, buf, len);
  }
}
  ;
}

此时服务器端如果使用lt模式:启动server,启动client,运行结果:

res 1

aaaa

res 1

bbbb

过5s ........

如果使用et模式 运行结果:

res 1

aaaa

过5s

res 1

bbbb

。。。。。。

所有当我们使用ET模式的时候,需要使用非阻塞的文件描述符。即对connfd使用fcntl改为非阻塞

即:

#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 8000
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;
  ];
  int res, len;
  efd = epoll_create();
/* ET 边沿触发 ,默认是水平触发 */
  event.events = EPOLLIN | EPOLLET;
/* event.events = EPOLLIN; */
  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));  /*fcntl可实现对指定文件描述符的各种操作,  F_GETFL读取文件状态标识*/
  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);
  ) {
    printf("epoll_wait begin\n");
    res = epoll_wait(efd, resevent, , -);
    printf("epoll_wait end res %d\n", res);
    ].data.fd == connfd) {
    )) > )
      write(STDOUT_FILENO, buf, len);
    }
  }
  ;
}

添加了以上代码;运行结果为:

epoll_wait begin

epoll_wait end res 1

aaaa

bbbb

epoll_wait begin

过5秒

epoll_wait end res 1

cccc

dddd

由此可以看出已经恢复了正常 。一次性全部读出了全部的数据,当没有数据的时候,read返回-1,结束循环继续监听。。。。

epoll ET(边缘触发) LT(水平触发)的更多相关文章

  1. epoll的边缘触发与水平触发

    epoll的边缘触发与水平触发 Tcp连接是双向的,内核为每个socket维护两个缓冲区,读缓冲区与写缓冲区,内核会一个关注这两个缓冲区,当采用水平触发时,对于写缓冲区而言,如果有多余空间可写,对于读 ...

  2. 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO

    一.基本概念                                                          我们通俗一点讲: Level_triggered(水平触发):当被监控的 ...

  3. epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO

    转自:http://www.cnblogs.com/yuuyuu/p/5103744.html 一.基本概念                                               ...

  4. Linux网络编程之select、poll、epoll的比较,以及epoll的水平触发(LT)和边缘触发(ET)

    Linux的网络通信先后推出了select.poll.epoll三种模式. select有以下三个问题: (1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大. ...

  5. Linux系统编程——水平触发和边沿触发

    事件模型 EPOLL事件有两种模型: Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据. Level Triggered (LT) 水平触发只要有数据都会触 ...

  6. jenkins 判断是手动触发还是定时器触发

    根据变量BUILD_CAUSE的值可以判断本次触发是手动触发还是定时器触发 手动触发:MANUALTRIGGER 定时器触发:TIMERTRIGGER

  7. Unity插件之NGUI学习(6)—— 关于Widget怎样加入触发事件(触发OnClick)

    NGUI中,Button本身就带有OnClick事件,可是Sprite,Label等( 也绑有Widget的)并没有触发事件,事实上NGUI的事件触发都必须加入Box Collider,并勾选Is T ...

  8. EasyUI触发方法、触发事件、创建对象的格式??

    创建对象 $("选择器").组件名({ 属性名 : 值, 属性名 : 值 }); 触发方法 $("选择器").组件名("方法名",参数); ...

  9. 利用epoll写一个"迷你"的网络事件库

    epoll是linux下高性能的IO复用技术,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率.另一点原因就是获取 ...

随机推荐

  1. TomCat的安装及测试

    1.每个版本的安装都是一样,解压之后是一个文件夹 2.配置环境变量,右击我的电脑,属性--高级属性设置--环境变量--新建--配path即可(path后加;%CATALINA_HOME%\bin;) ...

  2. c#二进制移位运算符 "<<"及">>","&","|","^"

    移位运算符 & :按位与,其实与&&逻辑运算符有一致的地方: | :按位或,同样与||有类似的地方. ~:按位取反 ^:按位异或 <<:左移运算符 >> ...

  3. python 实例四

    https://www.cnblogs.com/evablogs/p/6754981.html 题目:输入某年某月某日,判断这一天是这一年的第几天? 程序分析: 月份天数: 月份 天数 2 平年28天 ...

  4. c/c++ 多线程 等待一次性事件 异常处理

    多线程 等待一次性事件 异常处理 背景:假设某个future在等待另一个线程结束,但是在被future等待的线程里发生了异常(throw一个异常A),这时怎么处理. 结果:假设发生了上面的场景,则在调 ...

  5. Win10 - MySQL 5.7 密码重置

    Win10 - MySQL 5.7 密码重置 所有行为均发生在系统管理员权限的 Cmd 或 Powershell 下 注意! 本行为会导致数据库重置 # 重新安装 mysql 服务 mysqld -- ...

  6. jdbc链接数据库

    JDBC简介 JDBC全称为:Java Data Base Connectivity (java数据库连接),可以为多种数据库提供填统一的访问.JDBC是sun开发的一套数据库访问编程接口,是一种SQ ...

  7. javaScript判断手机型号

    window.onload = function () { alert("1"); var u = navigator.userAgent; if (u.indexOf('Andr ...

  8. ideal中把项目打成war包,并放在tomcat运行,遇见的问题。。。

    先说下我遇见的问题吧:最近做项目要把项目放在tomcat上运行,用的springboot框架, 在建项目时选择的是  jar包,项目写完要部署打包是,在pom中虽然把包改成了war ,可是每次放入to ...

  9. 【转】在子线程中new Handler报错--Can't create handler inside thread that has not called Looper.prepare()

    在子线程中new一个Handler为什么会报以下错误? java.lang.RuntimeException:  Can't create handler inside thread that has ...

  10. warn_alloc():page allocation failure问题分析

    关键词:warn_alloc().__GFP_XXX.order.CMA等等. 在内存申请的时候经常会遇到类似“ xxx: page allocation failure: order:10...”类 ...