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. geeksforgeeks-Array-Rotation and deletion

      As usual Babul is again back with his problem and now with numbers. He thought of an array of numb ...

  2. MySQL— 索引

    目录 一.索引 二.索引类型 三.索引种类 四.操作索引 五.创建索引的时机 六.命中索引 七.其它注意事项 八.LIMIT分页 九.执行计划 十.慢查询日志 一.索引 MySQL索引的建立对于MyS ...

  3. freeRTOS中文实用教程6--错误排查

    1.前言 本章主要是为刚接触FreeRTOS 的用户指出那些新手通常容易遇到的问题.这里把最主要的篇幅放在栈溢出以及栈溢出侦测上 2.printf-stdarg.c 当调用标准C 库函数时,栈空间使用 ...

  4. AT91RM9200---定时器简介

    1.前言 系统定时器模块集成了3个不同的定时器 一个周期性间隔的定时器,用来为操作系统设置时基 一个看门狗定时器,可用于软件死锁时进行系统复位 一个实时时钟计数器用来记录流逝的时间 系统定时器时钟 这 ...

  5. c# 取本地ip地址

    public static System.Net.IPAddress[] GetIpAddress() { string hostName = System.Net.Dns.GetHostName() ...

  6. html5 postMessage解决跨域、跨窗口消息传递(转)

    仅做学习使用,原文链接:http://www.cnblogs.com/dolphinX/p/3464056.html 一些麻烦事儿 平时做web开发的时候关于消息传递,除了客户端与服务器传值还有几个经 ...

  7. Z-index研究 opacity和z-index一起使用

    Z-index研究 opacity和z-index一起使用   关于z-index的真正问题是,很少有人理解它到底是怎么用.其实它并不复杂,但是如果你从来没有花一定时间去看具体的z-index相关文档 ...

  8. js获取iframe中的元素

    var obj=document.getElementById("iframe的name").contentWindow; var ifmObj=obj.document.getE ...

  9. poj3579 二分套二分

    和poj3685类似,都是二分答案然后在判断时再二分 这题的内层二分可以用stl代替 /* 二分套二分,思路:升序排序数据,先二分答案x进行判断,判断时枚举每个元素,二分找到和其之差小于等于x的所有值 ...

  10. AC自动机(trie图版)

    AC自动机是一个多模字符串匹配的自动机(网上说的),主要作用是在一个长串中同时进行多个字符串的匹配 基础芝士: trie树(字典树) 烤馍片kmp单模字符串匹配 如果不会的建议去网上学一下(本篇讲解略 ...