epoll介绍

epoll的行为与poll(2)相似,监视多个有IO事件的文件描述符。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

epoll_create(2) 创建一个新的epoll实例,并返回一个引用该实例的文件描述符

epoll_ctl(2) 创建epoll实例后,注册对感兴趣的文件描述符。当前注册在epoll实例上的文件描述符集被称为epoll集合。

epoll_wait(2) 等待I/O事件,如果当前没有事件可用,则阻塞调用线程。

水平触发和边沿触发

epoll事件分布接口既可以表现为边缘触发(ET),也可以表现为水平触发(LT)。这两种机制的区别

可以这样描述。假设有这种情况发生:

表示管道(rfd)的读侧的文件描述符在epoll实例上注册。

管道写入器在管道的写入端写入2 kB的数据。

调用epoll_wait(2)将返回rfd作为就绪文件描述符。

管道读取器从rfd读取1kb的数据。

epoll_wait(2)调用完成。

如果使用边缘触发标志将rfd文件描述符注册到epoll接口,那么第五步的epoll_wait(2)的调用可能会挂起,尽管文件输入缓冲区仍然有1kb数据可读;同时,远程对等端可能正在期望基于它已发送的数据的应答。这样做的原因是,只有在被监视文件描述符上发生更改时,边缘触发模式才交付事件。因此,在步骤5中,调用者可能会以等待那些仍在输入缓冲区中的数据的状态下结束。

在上面的例子中,将生成rfd上的一个事件,因为在2中完成了写入,而在3中使用了该事件。由于在4中完成的读操作不会消耗整个缓冲区数据,所以在步骤5中完成的对epoll_wait(2)的调用可能会无限期阻塞。

使用EPOLLET标志的应用程序应该使用非阻塞文件描述符,以避免在处理多个文件描述符时出现有阻塞的读写饥饿任务。建议使用epoll作为边沿触发(EPOLLET)接口的方式如下:

i、 具有非阻塞文件描述符

ii、只有在read(2)或write(2)返回EAGAIN后才等待事件。

相反,当EPOLLET作为水平触发接口使用时(默认情况下,没有指定EPOLLET), epoll只是一个更快的poll(2),并且可以在使用后者的任何地方使用,因为它具有相同的语义。

Epoll的优点:

1、支持一个进程打开大数目的socket描述符(FD)

select能打开的文件描述符有一定的限制,FD_SETSIZE设置,默认值是2048,有两种解决方法,1、修改它的值,然后重新编译内核。2、使用多进程加入要并发20w个客户,那么就要开100进程;epoll则没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是2万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

2、IO效率不随FD数目增加而线性下降

select/poll采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;内核 / 用户空间内存拷贝问题,select/poll需要复制大量的句柄数据结构,产生巨大的开销;select/poll返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。

3、支持边缘触发模式

select/poll的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select/poll调用还是会将这些文件描述符通知进程。

4、使用mmap加速内核与用户空间的消息传递。

select/poll和epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝很重要,在这点上,select/poll需要复制整个FD数组,产生巨大的开销;而epoll是通过内核于用户空间mmap同一块内存实现的。

epoll的系统调用

epoll_create

int epoll_create(int size);

int epoll_create1(int flags);

创建一个epoll的句柄。自从linux2.6.8之后,size参数是被忽略的,更推荐使用epoll_crete1(0)来替代,flags可以设置EPOLL_CLOEXEC标志

epoll_ctl

#include

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

该系统调用对文件描述符epfd引用的epoll(7)实例执行控制操作。它请求对目标文件描述符fd执行操作op。

epfd : epoll_create创建的文件描述符.

op :参数的有效参数为:

EPOLL_CTL_ADD

在文件描述符epfd引用的epoll实例上注册目标文件描述符fd。

EPOLL_CTL_MOD

修改已注册描述符fd关联的事件。

EPOLL_CTL_DEL

从epfd引用的epoll实例中删除(取消注册)目标文件描述符fd。该事件将被忽略,并且可以是NULL

fd :待监听的fd

epoll_event : 描述链接到文件描述符fd的对象,它的定义如下

typedef union epoll_data {

void *ptr;

int fd;

uint32_t u32;

uint64_t u64;

} epoll_data_t;

struct epoll_event {

uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

events成员是由以下可用事件类型的零个或多个组合在一起组成的位掩码:

EPOLLIN :关联的文件描述符可以读(包括对端SOCKET正常关闭);

EPOLLOUT:关联的文件描述符可以写;

EPOLLPRI:关联的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

EPOLLERR:关联的文件描述符发生错误;

EPOLLHUP:关联的文件描述符被挂断;

EPOLLRDHUP:流套接字对等关闭连接,或半关闭写。(当使用边缘触发监视时,此标记对于编写简单代码检测对等端是否关闭特别有用。2.6.17引入)

EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个fd的话,需要再次把这个fd加入到EPOLL队列里

它们在内核头文件里的定义如下:

33

34 enum EPOLL_EVENTS

35 {

36 EPOLLIN = 0x001,

37 #define EPOLLIN EPOLLIN

38 EPOLLPRI = 0x002,

39 #define EPOLLPRI EPOLLPRI

40 EPOLLOUT = 0x004,

41 #define EPOLLOUT EPOLLOUT

42 EPOLLRDNORM = 0x040,

43 #define EPOLLRDNORM EPOLLRDNORM

44 EPOLLRDBAND = 0x080,

45 #define EPOLLRDBAND EPOLLRDBAND

46 EPOLLWRNORM = 0x100,

47 #define EPOLLWRNORM EPOLLWRNORM

48 EPOLLWRBAND = 0x200,

49 #define EPOLLWRBAND EPOLLWRBAND

50 EPOLLMSG = 0x400,

51 #define EPOLLMSG EPOLLMSG

52 EPOLLERR = 0x008,

53 #define EPOLLERR EPOLLERR

54 EPOLLHUP = 0x010,

55 #define EPOLLHUP EPOLLHUP

56 EPOLLRDHUP = 0x2000,

57 #define EPOLLRDHUP EPOLLRDHUP

58 EPOLLEXCLUSIVE = 1u << 28,

59 #define EPOLLEXCLUSIVE EPOLLEXCLUSIVE

60 EPOLLWAKEUP = 1u << 29,

61 #define EPOLLWAKEUP EPOLLWAKEUP

62 EPOLLONESHOT = 1u << 30,

63 #define EPOLLONESHOT EPOLLONESHOT

64 EPOLLET = 1u << 31

65 #define EPOLLET EPOLLET

66 };

67

68

69 /* Valid opcodes ( "op" parameter ) to issue to epoll_ctl(). */

70 #define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface. */

71 #define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface. */

72 #define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure. */

epoll_wait

#include

int epoll_wait(int epfd, struct epoll_event *events,

int maxevents, int timeout);

int epoll_pwait(int epfd, struct epoll_event *events,

int maxevents, int timeout,

const sigset_t *sigmask);

等待在epoll监控的事件中已经发生的事件。

epfd : epoll_create() 的返回值.

events : 分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)

maxevents : maxevents告知内核这个events有多大,这个 maxevents的值大于0(否则Error :Invalid argument)

timeout : 超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时,它会阻塞直到

一个文件描述符有事件发生;

信号处理器中断;

超时;

epoll示例程序

此程序简单测试一下三个API,注册标准输出的描述符到epoll,监视标准输出的读事件,触发后回显一遍,quit退出程序.

#include

#include

#include

#include

#include

#include

#include

#include

#include

typedef std::vector PollFdList;

int main(int argc ,char **argv)

{

int fd;

char buf[1024];

int i,res,real_read, maxfd;

if((fd=open("/dev/stdin",O_RDONLY|O_NONBLOCK)) < 0)

{

fprintf(stderr,"open data1 error:%s",strerror(errno));

return 1;

}

PollFdList m_pollfds;

int epfd = epoll_create1(EPOLL_CLOEXEC);

struct epoll_event ev;

ev.events = EPOLLIN | EPOLLPRI;

ev.data.fd = fd;

epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);

m_pollfds.resize(1024);

while(1)

{

int ret = epoll_wait(epfd, m_pollfds.data(), m_pollfds.size(), 5000);

if (ret < 0)

{

printf("ePoll error : %s\n",strerror(errno));

return 1;

}

if(ret == 0){

printf("ePoll timeout\n");

continue;

}

for (i = 0; i< 1; i++)

{

if (m_pollfds[i].events & EPOLLIN)

{

memset(buf, 0, 1024);

real_read = read(m_pollfds[i].data.fd, buf, 1024);

if (real_read < 0)

{

if (errno != EAGAIN)

{

printf("read eror : %s\n",strerror(errno));

continue;

}

}

else if (!real_read)

{

close(m_pollfds[i].data.fd);

m_pollfds[i].events = 0;

}

else

{

if (i == 0)

{

buf[real_read] = '\0';

printf("%s", buf);

if ((buf[0] == 'q') || (buf[0] == 'Q'))

{

printf("quit\n");

return 1;

}

}

else

{

buf[real_read] = '\0';

printf("%s", buf);

}

}

}

}

}

exit(0);

}

./test

hello

hello

hello epoll

hello epoll

ePoll timeout

quit

quit

quit

epoll监听文件_epoll使用详解相关推荐

  1. epoll监听文件_epoll详解——从功能到内核

    首先我们了解一下什么是I/O复用.I/O就是指网络中的I/O(即输入输出),多路是指多个TCP连接,复用是指一个或少量线程被重复使用.连起来理解就是,用少量的线程来处理网络上大量的TCP连接中的I/O ...

  2. Java监听mysql的binlog详解(mysql-binlog-connector)

    Java监听mysql的binlog详解(mysql-binlog-connector) 1. 需求概述 2. 技术选型 3. 方案设计 3.环境准备 3.1 查看是否开启binlog 3.2 mys ...

  3. Oracle11g安装教程、配置实例、监听、客户端程序详解_Windows篇

    Oracle11g安装教程.配置实例.监听.客户端程序详解_Windows篇 文章目录 Oracle11g安装教程.配置实例.监听.客户端程序详解_Windows篇 前言 一.数据库的安装前准备,前提 ...

  4. epoll监听文件_介绍一下 Android Handler 中的 epoll 机制?

    介绍一下 Android Handler 中的 epoll 机制? 目录: IO 多路复用 select.poll.epoll 对比 epoll API epoll 使用示例 Handler 中的 e ...

  5. epoll监听文件_【原创】万字长文浅析:Epoll与Java Nio的那些事儿

    " Epoll 是Linux内核的高性能.可扩展的I/O事件通知机制. 在linux2.5.44首次引入epoll,它设计的目的旨在取代既有的select.poll系统函数,让需要大量操作文 ...

  6. 监听器之jp@gc详解

    一.jp@gc - Actiive Threads Over Time 不同时间活动用户数量展示 下面是一个阶梯加压测试的图标   二.jp@gc - Transactions per Second ...

  7. 修改php-fpm监听端口,php-fpm配置详解

    php5.3自带php-fpm /usr/local/php/etc/php-fpm.conf pid = run/php-fpm.pid pid设置,默认在安装目录中的var/run/php-fpm ...

  8. bootstrap源码之滚动监听组件scrollspy.js详解

    其实滚动监听使用的情况还是很多的,比如导航居于右侧,当主题内容滚动某一块的时候,右侧导航对应的要高亮. 实现功能 1.当滚动区域内设置的hashkey距离顶点到有效位置时,就关联设置其导航上的指定项 ...

  9. Vue.js中 watch(深度监听-deep)原理以及详解

    handler方法和immediate属性 这里 watch 的一个特点是,最初绑定的时候是不会执行的,要等到 firstName 改变时才执行监听计算.那我们想要一开始就让他最初绑定的时候就执行改怎 ...

最新文章

  1. AI一分钟|外媒:AI将消灭资本主义;特斯拉再遭唱空:量产存疑外加事故不断...
  2. 永动机之永动机的客观存在
  3. C#入门教程-注释的使用
  4. 模式的秘密-观察者模式(二)
  5. java数组求和递归,js数组去重 数组拼接 替换数组中的指定值 递归数组 判断数组中是否存在指定值 数组求和 根据条件判数组值...
  6. LeetCode 1464. 数组中两元素的最大乘积
  7. Conversion of Continuous-Valued Deep Networks to Efficient Event-Driven Networks for Image
  8. android对象关系映射框架ormlite之一对多(OneToMany)
  9. python机器学习库sklearn——支持向量机svm
  10. HCIE-Security心得
  11. html静态网站基于数码类电商购物网站网页设计与实现共计30个页面.rar(项目源码+毕业设计+论文+答辩PPT)
  12. 图书管理系统数据库SQL设计思路
  13. 实战:入侵win10
  14. Poco C++类库使用说明
  15. VirtualBox报错:不能为虚拟电脑XXX打开一个新任务
  16. JeecgBoot 框架中实现路由跳转页面,其他页面接收参数等使用方法
  17. 画论22 韩拙《山水纯全集》
  18. JAVA google验证码kaptcha 属性配置大全
  19. JetpackCompose - Scaffold布局简要入门
  20. 颜水成刘嘉:机器学习与认知神经的火花碰撞

热门文章

  1. 深度学习之基于Tensorflow2.0实现Xception网络
  2. php高效下载文件,LinkCache
  3. 用js写出水仙花数,JS 水仙花数
  4. 283. Move Zeroes(数组篇)
  5. 修改autor后面邮箱_如何修改LOL手游昵称
  6. html预览图片的功能,javascript实现的图片预览功能
  7. moel vue 自定义v_vue如何在自定义组件中使用v-model
  8. azdb文件怎么打开_AZDBMappingSvcs.dll
  9. excel表格怎么调整行高和列宽_8个实用Excel小技巧,谁用谁说好
  10. C++ 重载运算符 继承 多态 (超详细)