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)。这两种机制的区别

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

  1. 表示管道(rfd)的读侧的文件描述符在epoll实例上注册。
  2. 管道写入器在管道的写入端写入2 kB的数据。
  3. 调用epoll_wait(2)将返回rfd作为就绪文件描述符。
  4. 管道读取器从rfd读取1kb的数据。
  5. 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 <sys/epoll.h>

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 <sys/epoll.h>

       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 <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <sys/epoll.h>
#include <vector> typedef std::vector<struct epoll_event> 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使用详解的更多相关文章

  1. epoll使用详解(精髓)

    epoll使用详解(精髓) epoll - I/O event notification facility 在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中 ...

  2. epoll机制详解

    epoll机制详解 大牛的详解 epoll详解 什么是epoll? epoll是为处理大批量句柄而作了改进的poll, 是性能最好的多路I/O就绪通知方法; 只有三个系统调用: epoll_creat ...

  3. epoll原理详解及epoll反应堆模型

    本文转载自epoll原理详解及epoll反应堆模型 导语 设想一个场景:有100万用户同时与一个进程保持着TCP连接,而每一时刻只有几十个或几百个TCP连接是活跃的(接收TCP包),也就是说在每一时刻 ...

  4. Epoll模型详解

    Linux 2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数. 1.为什么select落后    首先,在Lin ...

  5. epoll使用详解(精髓)(转)

    epoll - I/O event notification facility 在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是 ...

  6. (转)Epoll模型详解

    1. 内核中提高I/O性能的新方法epoll epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll.要使用epoll只需要这三个系统调 用:epoll_create(2),  ...

  7. epoll使用详解:epoll_create、epoll_ctl、epoll_wait、close

    epoll - I/O event notification facility 在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是 ...

  8. IO模型(epoll)--详解-03

    写在前面 epoll是开发linux高性能服务器的必备技术至,epoll本质,是服务端程序员的必须掌握的知识. 七.epoll的原理和流程 本节会以示例和图表来讲解epoll的原理和流程. 创建epo ...

  9. IO模型(epoll)--详解-02

    写在前面 从事服务端开发,少不了要接触网络编程.epoll作为linux下高性能网络服务器的必备技术至关重要,大部分游戏服务器都使用到这一多路复用技术.文章核心思想是:要让读者清晰明白EPOLL为什么 ...

随机推荐

  1. 实例详析ImageView的adjustViewBonds和scaleType

    android:adjustViewBounds是否保持宽高比.需要与maxWidth.MaxHeight一起使用,否则单独使用没有效果. 设置View的最大高度,单独使用无效,需要与setAdjus ...

  2. 在Mysql中查询两个时间段的差,可以是秒,天,星期,月份,年...

    SELECT TIMESTAMPDIFF(SECOND, now(), "2016-11-11 00:00:00") 语法为:TIMESTAMPDIFF(unit,datetime ...

  3. Java用System读取系统相关信息、环境变量——(六)

    package Java_Test; public class System1 { public static void main(String[] args) { // TODO Auto-gene ...

  4. C++学习3--编程基础(vector、string、三种传参)

    知识点学习 Vector容器 vector是C++标准程序库中的一个类,其定义于头文件中,与其他STL组件一样,ventor属于STD名称空间: ventor是C++标准程序库里最基本的容器,设计之初 ...

  5. python内置模块之collections(六)

    前言 collections是Python内建的一个集合模块,提供了许多有用的集合类. 系列文章 python模块分析之random(一) python模块分析之hashlib加密(二) python ...

  6. centos 命令和

    一.远程工具 Window系统上 Linux 远程登录客户端有SecureCRT, Putty, SSH Secure Shell.TightVNC... 重点推荐一款 FinallShell,一般人 ...

  7. nginx配置集群

    1.准备两个Tomcat 首先在Linux机器上部署两个Tomcat,端口分别为80和8080 2.分别部署测试应用 在两个tomcat下分别部署同一个应用testapp,很简单,就是在页面显示当前系 ...

  8. 安装最新版的2016版Pycharm后,激活码

    2016年安装Pycharm后,过段时间过期.亲测只需要复制以下激活码可以完美解决激活问题,又可以开心的写Python了.以下为激活码内容: BIG3CLIK6F-eyJsaWNlbnNlSWQiOi ...

  9. 并发研究之Java内存模型(Java Memory Model)

    Java内存模型JMM java内存模型定义 上一遍文章我们讲到了CPU缓存一致性以及内存屏障问题.那么Java作为一个跨平台的语言,它的实现要面对不同的底层硬件系统,设计一个中间层模型来屏蔽底层的硬 ...

  10. MySQL 由 5.7 升级为 8.0 之后,Laravel 的配置改动

    开发机上升级了 MySQL 8.0, 原有的 Laravel 5.5 项目就启动失败了. 报错信息是: [2018-05-30 11:17:37] local.ERROR: SQLSTATE[4200 ...