Event Poll epoll 详解

由于poll()和select()的局限,2.6内核引入了event poll(epoll)机制。虽然稍微复杂,但是epoll解决了它们共有的基本性能问题,并增加了一些新的特性。

poll()和select()每次调用都需要所有被监听的文件描述符。内核必须遍历所有被监视的文件描述符。当这个表变得很大时,成千上百的文件描述符,每次调用时的遍历就成为了明显的瓶颈。

1、创建一个新的epoll实例

使用epoll_create()或者epoll_cerate1()创建一个epoll上下文。这里epoll_cerate1()是epoll_cerate()的扩展版本。

#include <sys/epoll.h>
int epoll_create (int size)

调用成功后,epoll_create()创建一个epoll实例,返回与该实例关联的文件描述符。这个文件描述符和真正的文件没有关系,仅仅是为了后续调用使用epoll而创建的。size参数告诉内核需要监听的文件描述符数目,但不是最大值。传递一个适当的近似值会带来性能的提升,但不需要给出确切的数字。出错时,返回-1,设置errno为下列值之一:

EINVAL   size不是正数

ENFILE    系统达到打开文件数的上限

ENOMEN    没有足够内存完成该次操作。

标准调用如下:

int epfd;
epfd = epoll_create (100);
if (epfd <0 )
	perror("epoll_create");

epoll_create返回的文件描述符需要用close()关闭。

2、控制 epoll

epoll_ctl 可以向指定的epoll上下文加入或删除文件描述符:

#include <sys/epoll.h>
int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event);
头文件<sys/epoll.h>中定义了epoll event结构体

struct epoll_event {
	_u32 events;
	union {
	void * ptr;
	int fd;
	_u32 u32;
	_u64 u64;
	}data;
};
epoll_ctl()成功调用将关联epoll实例和epfd。参数op指定对fd要进行的操作。event参数描述epoll更具体的行为
    以下是参数op的有效值:
EPOLL_CTL_ADD 把fd指定的文件添加到epfd指定的epoll实例监听集中,监听event中定义的事件。
EPOLL_CTL_DEL 把fd指定的文件从epfd指定的epoll监听集中删除。
EPOLL_CTL_MOD 使用event改变在已有fd上的监听行为。
    epoll_event结构体中的event参数列出了在给定文件描述符上监听的事件。多个事件可以使用位或运算同时指定。以下是有效值:
EPOLLERR 文件出错。即使不设置这个标志,这个事件也是被监听的。
EPOLLET 使用边沿触发。默认是水平触发。
EPOLLHUP 文件被挂起。即使不设置这个标志,这个事件也是被监听的。
EPOLLIN 文件未阻塞,可读。
EPOLLONESHOT 在一次事件产生被处理之后,文件不在被监听。必须不再被监听。必须使用EPOLL_CTL_MOD指定新的事件,以便重新监听文件。
EPOLLOUT 文件未阻塞,可写。
EPOLLPRI 高优先级的带外数据可读。
    event_poll中的data字段由用户使用。确认监听事件后,data会被返回给用户。通常将event.data.fd设定为fd,这样就可以知道那个文件描述符触发事件。
    成功后,epoll_ctl()返回0.失败返回-1,并设置errno为下列值:
EBADF epfd不是一个有效的epoll实例,或者fd不是有效文件描述符。
EEXIST op为EPOLL_CTL_ADD,但是fd已经与epfd关联。
EINVAL epfd不是一个epoll实例,epfd和fd相同,或者op无效。
ENOENT op是EPOLL_CTL_MOD或者是EPOLL_CTL_DEL,但是fd没有与epfd关联。
ENOMEN 没有足够内存完成进程的请求。
EPERM fd不支持epoll。
     在epfd实例中加入一个fd指定的监听文件,使用如下代码:
struct epoll_event event;
int ret;
event.data.fd = fd;/*return the fd to us later*/
event.events = EPOLLIN|EPOLLOUT  ;
ret = epoll_ctl (epfd,EPOLL_CTL_MOD,fd,&event);
if (ret)
	perror ("epoll_ctl");
    修改epfd实例中的fd上的一个监听事件,可以使用如下代码:
struct epoll_event event;
int ret;
event.data.fd = fd;/*return the fd to us later*/
event.events = EPOLLIN ;
ret = epoll_ctl (epfd,EPOLL_CTL_MOD,fd,&event);
if (ret)
	perror ("epoll_ctl");
    删除一个fd监听事件,可以使用如下代码:
struct epoll_event event;
int ret;
event.data.fd = fd;/*return the fd to us later*/
event.events = EPOLLIN ;
ret = epoll_ctl (epfd,EPOLL_CTL_DEL,fd,&event);
if (ret)
	perror ("epoll_ctl");

3、等待Epoll事件

epoll_wait()等待给定epoll实例关联的文件描述符上的事件:
#include <sys/epoll.h>
int epoll_wait (int epfd, struct epoll_event *
	* events, int maxevents, int timeout);
对epoll_wait()的调用等待epoll实例epfd中的文件fd上的事件,时限为timeout毫秒。成功返回,events指向包含epoll_event结构体(该结构体描述了每个事件)的内存,
且最多可以有maxevents个事件。返回值是事件数,出错返回-1,并将errno设置为以下值
EBADF epfd是无效文件描述符
EFAULT 进程对events指向的内存无写权限
EINTR 系统调用在完成前被信号中断
EINVAL epfd不是有效的epoll实例,或者maxevents小于等于0
    如果timeout 为0.即使没有事件发生,调用也立即发生,此时调用返回0.如果timeout为-1,调用将一直等待到有事件发生。
    当调用epoll_wait()返回,epoll_event结构体中的events数组描述了一次等待发生的事件,最多返回maxevents个事件。data字段包含了用户在调用epoll_ctl前的设置,
如文件的句柄,用来区分那个文件所发生的事件。
    一个完整的epoll_wait()例子如下:
#define MAX_EVENTS   64
struct epoll_event * events = NULL;
int nr_events, i, epfd;
events = malloc (sizeof(struct epoll_event) * MAX_EVENTS);
if (! events ){
	perror("malloc");
	exit(-1);
}
nr_events = epoll_wait (epfd,events,MAX_EVENTS,-1);
if (nr_events < 0){
	perror("epoll_wait");
	free(events);
	exit (-1);
}
for (int i=0; i<nr_eventsl i++)
	printf("event = %d on fd = %d \n",
		events[i].events,events[i].data.fd);
 

4、边沿触发时间和水平触发事件

EPOLL事件有两种模型 Level Triggered (LT)Edge Triggered (ET):

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

ET(edge-triggered,边缘触发模式)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,等到下次有新的数据进来的时候才会再次出发就绪事件。

5、man epoll 中的实例

setnonblocking()函数将socket文件设置为非阻塞,因为使用的是ET模式。do_use_fd()是对此文件做出一定的处理,如读写等。

#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;

/* Set up listening socket, ‘listen_sock‘ (socket(),
    bind(), listen()) */

epollfd = epoll_create(10);
if (epollfd == -1) {
    perror("epoll_create");
    exit(EXIT_FAILURE);
}

ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
    perror("epoll_ctl: listen_sock");
    exit(EXIT_FAILURE);
}

for (;;) {
    nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
    if (nfds == -1) {
        perror("epoll_pwait");
        exit(EXIT_FAILURE);
    }

    for (n = 0; n < nfds; ++n) {
        if (events[n].data.fd == listen_sock) {
            conn_sock = accept(listen_sock,
                            (struct sockaddr *) &local, &addrlen);
            if (conn_sock == -1) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
            setnonblocking(conn_sock);
            ev.events = EPOLLIN | EPOLLET;
            ev.data.fd = conn_sock;
            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                        &ev) == -1) {
                perror("epoll_ctl: conn_sock");
                exit(EXIT_FAILURE);
            }
        } else {
            do_use_fd(events[n].data.fd);
        }
    }
}
时间: 2024-10-13 22:51:57

Event Poll epoll 详解的相关文章

select, poll, epoll详解(二)

1. Select源码解析 基于2.6.28内核代码,select主要包含4个函数. sys_select:处理时间参数,然后调用core_sys_select. core_sys_select:处理三个fd_set参数(in, out, ex),然后调用do_select. do_select:遍历所有的fd,做select/poll的工作.在合适的时机把自己挂起等待,然后调用sock_poll. sock_poll: 利用函数指针,来调用具体的文件系统poll函数,包括tcp_poll, u

Linux IO模式及 select、poll、epoll详解

注:本文是对众多博客的学习和总结,可能存在理解错误.请带着怀疑的眼光,同时如果有错误希望能指出. 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念: - 用户空间和内核空间 - 进程切换 - 进程的阻塞 - 文件描述符 - 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32

(转)Linux IO模式及 select、poll、epoll详解

本文为转载,并作了部门调整.修改. [原文出处:https://segmentfault.com/a/1190000003063859] 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念: 用户空间和内核空间 进程切换 进程的阻塞 文件描述符 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚

Linux IO模式及 select、poll、epoll详解(转载)

http://segmentfault.com/a/1190000003063859#articleHeader6   原文 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念:- 用户空间和内核空间- 进程切换- 进程的阻塞- 文件描述符- 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚拟

网络通信 --&gt; IO多路复用之select、poll、epoll详解

IO多路复用之select.poll.epoll详解 目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作.但select,pselect,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内

IO多路复用--epoll详解

epoll 或者 kqueue 的原理是什么? [转自知乎] Epoll 引入简介 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套接字,还是管道,我们都可以把他们看作流.之后我们来讨论I/O的操作,通过read,我们可以从流中读入数据:通过write,我们可以往流写入数据.现在假定一个情形,我们需要从流中读数据,但是流中还没有数据,(典型的例子为,客户端要从socket读如数据,但是服务器还没有把数据传回来),这时候该怎么办

epoll详解

欢迎转载,转载请注明原文地址:http://blog.csdn.net/majianfei1023/article/details/45772269 一.基本概念: 1.epoll是什么: epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率.另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入就

Linux下的I/O复用与epoll详解

前言 I/O多路复用有很多种实现.在linux上,2.4内核前主要是select和poll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术.尽管他们的使用方法不尽相同,但是本质上却没有什么区别.本文将重点探讨将放在EPOLL的实现与使用详解. 为什么会是EPOLL select的缺陷 高并发的核心解决方案是1个线程处理所有连接的“等待消息准备好”,这一点上epoll和select是无争议的.但select预估错误了一件事,当数十万并发连接存

jQuery Event.which 属性详解

jQuery Event.which 属性详解 which属性用于返回触发当前事件时按下的键盘按键或鼠标按钮. 对于键盘和鼠标事件,该属性用于确定你按下的是哪一个键盘按键或鼠标按钮. which属性对DOM原生的event.keyCode和event.charCode进行了标准化. 适用的事件类型主要有键盘事件:keypress.keydown.keyup,以及鼠标事件:mouseup.mousedown. 该属性属于jQuery的Event对象(实例). 语法 jQuery 1.1.3 新增该