epoll man手册解读

来源:互联网 发布:最近新疆网络咋回事 编辑:程序博客网 时间:2024/06/07 03:11

epoll man手册解读

1.epoll_create()

1.1.概要

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

1.2.描述

epoll_create()

epoll_create()创建一个epoll“实例”,size参数目前被忽略。epoll_create()返回一个引用新的epoll实例的文件描述符。当不再需要时,应该使用close()关闭epoll_create()返回的文件描述符。

epoll_create1()

如果falgs为0,epoll_create1()与epoll_create()相同。标志中可以包含以下值以获得不同的行为:EPOLL_CLOEXEC    在新的文件描述符上设置关闭执行(FD_CLOEXEC)标志。 有关这可能有用的原因,请参阅open(2)中的O_CLOEXEC标志的描述。

1.3.返回值

成功时:返回一个非负文件描述符;错误时:返回-1,并将errno设置为指示错误。

1.4.错误

EINVAL size设置出错。

2.epoll_wait()

2.1.概要

#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);

2.2.描述

epoll_wait()

epoll_wait()系统调用等待文件描述符epfd引用的epoll实例上的事件。 maxevents参数必须大于零。该调用超时等待时间为timeout毫秒。timeout为-1时,会使epoll_wait()无限期地等待;timeout为 0时,即使没有可用的事件(返回码等于零),epoll_wait()也会立即返回。struct epoll_event定义如下:    typedef union epoll_data    {        void    *ptr;        int      fd;        uint32_t u32;        uint64_t u64;    } epoll_data_t;    struct epoll_event    {        uint32_t     events;    /* epoll事件 */        epoll_data_t data;      /* 用户数据变量 */    };The data of each returned structure will contain the same datathe user set with an epoll_ctl(2) (EPOLL_CTL_ADD,EPOLL_CTL_MOD)while the events member will contain the returned event bit field.event成员可以下面的事件类型进行位运算“|”组成:    EPOLLIN        相关文件可用于read(2)操作。    EPOLLOUT        相关文件可用于write(2)操作。    EPOLLRDHUP(自Linux 2.6.17开始)        流套接字对等封闭连接,或关闭写的一半连接。        (这个标志对于编写简单的代码特别有用,在使用边缘触发监视时检测对等体关闭。)    EPOLLPRI        有可用于read(2)操作的紧急数据。    EPOLLERR        关联的文件描述符发生错误情况。        epoll_wait(2)总是等待这个事件; 没有必要将它设置为事件。    EPOLLHUP        在相关的文件描述符上发生挂起。        epoll_wait(2)总是等待这个事件; 没有必要将它设置为事件。    EPOLLET        设置相关文件描述符的边缘触发行为。        epoll的默认行为是水平触发级别。        有关Edge和Level Triggered事件分布架构的更多详细信息,请参阅epoll(7)。    EPOLLONESHOT(自Linux 2.6.2起)        设置相关文件描述符的单次行为。        这意味着在用epoll_wait(2)提取事件之后,相关的文件描述符被内部禁用,并且epoll接口不会报告其他事件。        用户必须使用EPOLL_CTL_MOD调用epoll_ctl()来重新构建新的事件掩码的文件描述符。

epoll_pwait()

epoll_wait()和epoll_pwait()之间的关系类似于select(2)和pselect(2)之间的关系:像pselect(2)。epoll_pwait()允许应用程序安全地等待,直到文件描述符准备就绪或直到信号被捕获。以下epoll_pwait()调用:    ready = epoll_pwait(epfd,&events,maxevents,timeout,&sigmask);相当于原子地执行以下调用:    sigset_t origmask;    sigprocmask(SIG_SETMASK,&sigmask,&origmask);    ready = epoll_wait(epfd,&events,maxevents,timeout);    sigprocmask(SIG_SETMASK,&origmask,NULL);sigmask参数可以指定为NULL,在这种情况下epoll_pwait()等价于epoll_wait()。

2.3.返回值

成功时:epoll_wait()返回为所请求的I/O准备好的文件描述符数;       如果在请求的超时毫秒期间没有文件描述符就绪,则返回0;错误时:epoll_wait()返回-1, 并且适当设置errno。

2.4.错误

EBADF epfd不是有效的文件描述符。EFAULT 事件指向的内存区域无写权限。EINTR 在发生任何请求的事件或超时到期之前,该调用被信号处理程序中断,参见信号(7)。EINVAL epfd不是epoll文件描述符,或maxevents小于或等于零。

3.epoll_ctl()

3.1.概要

#include <sys/epoll.h>int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

3.2.描述

该系统调用对由文件描述符epfd引用的epoll实例执行控制操作。 它要求对目标文件描述符fd执行op操作。op参数的有效值为:EPOLL_CTL_ADD    在文件描述符epfd引用的epoll实例上注册目标文件描述符fd,并将event关联内部文件链接到fd。EPOLL_CTL_MOD    更改与目标文件描述符fd相关联的event事件。EPOLL_CTL_DEL    从epfd引用的epoll实例中删除目标文件描述符fd。如果event设为NULL,则会被忽略。

3.3.返回值

成功时:epoll_ctl()返回0;错误时:epoll_ctl()返回-1,并且适当地设置errno。

3.4.错误

EBADF epfd或fd不是有效的文件描述符。EEXIST op是EPOLL_CTL_ADD,但是提供的文件描述符fd已经在该epoll实例中注册。EINVAL epfd不是epoll文件描述符,或fd与epfd相同,或者该接口不支持请求的操作op。ENOENT op为EPOLL_CTL_MOD或EPOLL_CTL_DEL,并且fd未在此epoll实例上注册。ENOMEM 没有足够的内存来处理请求的op控制操作。尝试在epoll实例上注册(EPOLL_CTL_ADD)一个新的文件描述符时遇到/proc/sys/fs/epoll/max_user_watches施加的限制。有关详细信息,请参阅epoll(7)。EPERM 目标文件fd不支持epoll。

3.5.bug

在2.6.9之前的内核版本中,EPOLL_CTL_DEL操作在event中需要一个非空指针,即使该参数被忽略。自从Linux 2.6.9,使用EPOLL_CTL_DEL时可以将event指定为NULL。在2.6.9之前需要移植到内核的应用程序应该在event中指定一个非NULL指针。

4.综述

epoll - I / O事件通知工具

4.1.描述

epoll的使用流程

epoll是poll(2)的变体,可以用作边缘触发或水平触发的接口,并可以很好地扩展到大量的监视文件描述符。提供以下系统调用来创建和管理一个epoll实例:    (1).由epoll_create(2)创建的epoll实例,它返回引用epoll实例的文件描述符。       (最近的epoll_create1(2)扩展了epoll_create(2)的功能)    (2).通过epoll_ctl(2)注册对特定文件描述符的兴趣。        当前在epoll实例上注册的文件描述符集合有时被称为epoll集。    (3).最后,实际的等待由epoll_wait(2)开始。

Level-Triggered and Edge-Triggered

epoll事件分发接口能够支持边缘触发(ET)和水平触发(LT)。两种机制之间的区别可以描述如下。 假设这种情况发生:    1.表示管道读端的文件描述符(rfd)已注册在epoll实例上。    2.从管道的写端写入2kB的数据。    3.epoll_wait(2)的调用将(rfd)作为准备就绪的文件描述符返回。    4.管道读端从(rfd)读取1kB的数据。    5.调用epoll_wait(2)完成。如果使用EPOLLET(边缘触发)标志将(rfd)文件描述符添加到epoll接口,尽管文件输入缓冲区中仍然存在可用数据,但步骤5完成后,对epoll_wait(2)的调用可能会挂起;同时远端的对等体可能会根据已经发送的数据在期待响应。这是因为边缘触发模式只在监视的文件描述符发生更改时传递事件。因此,在步骤5中,调用者可能会放弃输入缓冲区中已经存在的一些数据。在上面的示例中,由于在2中完成写入,并且事件在3中消耗,所以将生成(rfd)上的事件。由于4中完成的读取操作未消耗整个缓冲区数据,所以在步骤5中完成的对epoll_wait(2)的调用可能会无限期地阻止。

EPOLLET

使用EPOLLET标志的应用程序应使用非阻塞文件描述符,以避免阻塞读取或写入使处理多个文件描述符的任务被饿死。使用epoll作为边缘触发(EPOLLET)接口的建议方法如下:    1.使用非阻塞文件描述符;    2.直到EAGAIN被read(2)或write(2)返回才退出。

EPOLLLT

相比之下,当用作水平触发的接口(默认情况下,当EPOLLET未指定时),epoll只是一种更快的poll(2),并且可以在后者被使用的地方使用,因为它共享相同的语义。

EPOLLONESHOT

由于即使使用边缘触发的epoll,在接收到多个数据块时也可以生成多个事件,调用者可以选择指定EPOLLONESHOT标志,以便在epoll_wait(2)收到事件之后告诉epoll禁用相关的文件描述符。当指定EPOLLONESHOT标志时,调用者有责任使用epoll_ctl(2)与EPOLL_CTL_MOD重新配置文件描述符。

/proc接口限制

/proc接口以下接口可用于限制epoll消耗的内核内存量:/proc/sys/fs/epoll/max_user_watches(自Linux 2.6.28开始)这指定用户可以在系统上的所有epoll实例上注册的文件描述符总数的限制。限制是根据实际的用户ID。 每个注册的文件描述符在32位内核上大致为90字节,在64位内核上大约为160字节。目前,max_user_watches的默认值是可用最低内存的1/25(4%),除以注册成本(以字节为单位)。 

4.2.Example

虽然在用作级别触发接口时使用epoll确实具有与poll(2)相同的语义,但是边缘触发使用需要更多的澄清以避免应用程序事件循环中的停顿。在这个例子中,监听器是一个【非阻塞套接字】,其中已经调用了listen(2)。函数do_use_fd()使用新的就绪文件描述符,直到EAGAIN被read(2)或write(2)返回。事件驱动的状态机应用程序应在接收到EAGAIN后记录其当前状态,以便在下一次调用do_use_fd()时,它将继续read(2)或write(2)。

示例程序

#define MAX_EVENTS 10struct 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; // listen_sock是否需要使用EPOLLET?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 (;;){    // timeout为-1时,会使epoll_wait()无限期地等待    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);        }    }}

当用作边缘触发接口时,出于性能原因,可以通过指定(EPOLLIN | EPOLLOUT)将epoll接口(EPOLL_CTL_ADD)中的文件描述符添加一次。
避免在EPOLLIN和EPOLLOUT之间连续切换,通过EPOLL_CTL_MOD调用epoll_ctl(2)。

4.3.Questions and Answers

问题零

Q:用于区分在epoll注册的文件描述符的标示是什么?A:文件描述符号和打开的文件描述(也称为“打开文件句柄”,内核的打开文件的内部表示形式)的组合。

问题一

Q:如果在epoll实例上注册相同的文件描述符两次,会发生什么?A:你可能会得到EEXIST。 然而,可以向相同的epoll实例添加一个重复(dup(2),dup2(2),fcntl(2)F_DUPFD)描述符。   如果重复的文件描述符使用不同的事件掩码进行注册,这可能是过滤事件的有用技术。

问题二

Q:两个epoll实例可以等待相同的文件描述符吗? 如果是这样,事件是否报告给这两个epoll文件描述符?A:是的,事件将被报告给两者。 但是,可能需要仔细编程才能正确执行。

问题三

Q:epoll文件描述符本身是否poll/epoll可选?A:是的 如果epoll文件描述符有事件等待,那么它将被指示为可读。

问题四

Q:如果尝试将epoll文件描述符放入自己的文件描述符集中会发生什么?A:epoll_ctl(2)调用将失败(EINVAL)。 但是,您可以在另一个epoll文件描述符集中添加epoll文件描述符。

问题五

Q:我可以通过Unix域套接字将epoll文件描述符发送到另一个进程吗?A:是的,但是这样做是没有意义的,因为接收过程不会在epoll集合中包含文件描述符的副本。

问题六

Q:关闭文件描述符会使其从所有epoll集中自动删除吗?A:是的,但请注意以下几点。  文件描述符是对打开的文件描述的引用(参见open(2))。  每当通过dup(2),dup2(2),fcntl(2)F_DUPFD或fork(2)复制描述符时,  创建引用相同打开文件描述的新文件描述符。  打开的文件描述继续存在,直到引用它的所有文件描述符都已关闭。  只有在引用底层打开的文件描述的所有文件描述符已经关闭之后 (或者如果使用epoll_ctl()EPOLL_CTL_DEL)显式删除描述符之前,才会从epoll集合中删除文件描述符。  这意味着即使在作为epoll集合的一部分的文件描述符被关闭之后,  如果引用相同的底层文件描述的其他文件描述符保持打开,那么也可以为该文件描述符报告事件。

问题七

Q:如果在epoll_wait(2)调用之间发生多个事件,它们是否组合或单独报告?A:将合并。

问题八

Q:对文件描述符的操作是否影响已经收集但尚未报告的事件?A:您可以对现有文件描述符执行两个操作。 对于这种情况,删除将是无意义的。 修改将重新读取可用的I/O。

问题九

Q:使用EPOLLET标志(边缘触发)时,是否需要持续读/写文件描述符至EAGAIN?A:从epoll_wait(2)接收事件应该暗示您:这个文件描述符已就绪进行I/O操作。  ( You must consider it ready until the next (non-blocking) read/write yields EAGAIN.)   您必须考虑它的就绪状态,直到下一个(非阻塞)read/write生EAGAIN。    使用文件描述符的时间和方式完全取决于您。   (1).对于分组/面向令牌的文件(例如,数据报套接字,规范模式下的终端),检测I/O空间结束的唯一方法是继续读/写直到EAGAIN。   (2).对于面向流的文件(例如管道,FIFO,流套接字),也可以通过检查从目标文件描述符读取的数据量或写入目标文件描述符的数据量来检测I/O空间耗尽的情况。        例如,如果调用read(2)要求读取一定量的数据,但是read(2)返回较少的字节数,则可以确保已耗尽文件描述符的I/O空间。        使用write(2)写入时也是如此。(如果不能保证受监视的文件描述符始终引用一个面向流的文件,请避免使用这种技术。)

4.4.可能的陷阱和规避方法

饥饿(边缘触发)

如果有大量的I/O,如果尽力处理一个文件描述符,其他的文件描述符得不到处理,会导致饥饿。(这个问题不是特定于epoll。)解决方案是维护一个就绪列表,并将文件描述符在其关联的数据结构中标记为就绪状态。从而允许应用程序记住那些文件需要被处理但仍处于就绪状态的文件句柄。你也可忽略那些已通知你就绪的文件描述符。

如果使用事件缓存

如果您使用事件缓存或存储从epoll_wait(2)返回的所有文件描述符,请确保提供一种动态标记其关闭(即由先前事件处理引起的)的方法。假设您从epoll_wait(2)收到100个事件,而在事件#47中,一个条件导致事件#13被关闭。如果删除结构并关闭事件#13的文件描述符,那么事件缓存可能仍然会说有事件等待该文件描述符,从而导致混淆。一个解决方案是在事件47的处理期间调用epoll_ctl(EPOLL_CTL_DEL)来删除文件描述符13并close(2),然后将其关联的数据结构标记为已删除,并将其链接到清除列表。如果在批处理中找到文件描述符13的另一个事件,您将发现文件描述符以前已被删除,并且不会有任何混淆。

5.重点强调

(1). listenfd是否需要使用EPOLLET

(2). epoll_wait()的timeout参数

timeout 超时等待时间(毫秒)timeout为-1时,会使epoll_wait()无限期地等待;timeout为 0时,即使没有可用的事件(返回码等于零),epoll_wait()也会立即返回。

(3). 使用epoll作为边缘触发(EPOLLET)接口的建议方法如下:

使用非阻塞文件描述符;直到EAGAIN被read(2)或write(2)返回才退出。

(3). epoll_ctl()

当用作边缘触发接口时,出于性能原因,可以通过指定(EPOLLIN | EPOLLOUT)将epoll接口(EPOLL_CTL_ADD)中的文件描述符添加一次。避免在EPOLLIN和EPOLLOUT之间连续切换,通过EPOLL_CTL_MOD调用epoll_ctl(2)。

(4). 可能的陷阱和规避方法

饥饿(边缘触发)事件缓存
0 0
原创粉丝点击