epoll

发布时间 2024-01-06 10:28:50作者: OzTaking

1.man epoll

arm@arm:~$ man epoll
arm@arm:~$ man epoll
EPOLL(7)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              Linux Programmer's Manual                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             EPOLL(7)

NAME
       epoll - I/O event notification facility
       /*I/O 事件通知设施*/

SYNOPSIS
       #include <sys/epoll.h>

DESCRIPTION
       The epoll API performs a similar task to poll(2): monitoring multiple file descriptors to see if I/O is possible on any of them.  
       The epoll API can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watched file descriptors.
       /*epoll API执行与poll(2)类似的任务:监视多个文件描述符,以查看其中任何一个文件描述符上是否可以进行I/O。          
       epoll API既可以用作边缘触发接口,也可以用作水平触发接口,并且可以很好地扩展到大量被监视的文件描述符。*/

       The central concept of the epoll API is the epoll instance, an in-kernel data structure which, from a user-space perspective,
        can be considered as a container for two lists:
        /*epoll API的核心概念是epoll实例,这是一个内核内的数据结构,从用户空间的角度来看,可以看作是两个列表的容器:*/

       *   The interest list (sometimes also called the epoll set): the set of file descriptors that the process has registered an interest in monitoring.

       *   The ready list: the set of file descriptors that are "ready" for I/O.  
       The ready list is a subset of (or, more precisely, a set of references to) the file descriptors in the interest list that is dynamically populated 
       by the kernel as a result of I/O activity on those file descriptors.
       /*
       * 兴趣列表(有时也称为epoll集):进程已注册有兴趣监视的文件描述符集。
       * ready list:准备好I/O的文件描述符集合。  
       * 就绪列表是兴趣列表中的文件描述符的子集(或者更准确地说,是一组引用),由内核动态填充,作为这些文件描述符上的I/O活动的结果。
       */


       The following system calls are provided to create and manage an epoll instance:

       *  epoll_create(2) creates a new epoll instance and returns a file descriptor referring to that instance.  
       (The more recent epoll_create1(2) extends the functionality of epoll_create(2).)

       *  Interest in particular file descriptors is then registered via epoll_ctl(2), which adds items to the interest list of the epoll instance.

       *  epoll_wait(2) waits for I/O events, blocking the calling thread if no events are currently available.  
       (This system call can be thought of as fetching items from the ready list of the epoll instance.)

        /*
        以下系统调用用于创建和管理epoll实例:
       *  epoll_create(2)创建一个新的epoll实例,并返回一个引用该实例的文件描述符。 (最近的epoll_create1(2)扩展了epoll_create(2)的功能)。

       *  然后通过epoll_ctl(2)注册对特定文件描述符的兴趣,它将项目添加到epoll实例的兴趣列表中。

       *  epoll_wait(2)等待I/O事件,如果当前没有可用的事件,则阻塞调用线程。 (系统调用可以被认为是从epoll实例的就绪列表中获取项目)。
        */


   Level-triggered and edge-triggered /*水平触发 和边沿触发*/
       The epoll event distribution interface is able to behave both as edge-triggered (ET) and as level-triggered (LT).  
       The difference between the two mechanisms can be described as follows.  Suppose that this scenario happens:
       /*
       epoll事件分发接口能够表现为边沿触发(ET)和电平触发(LT)。          
       这两种机制之间的区别可以描述如下。  
       假设这种情况发生:
       */

       1. The file descriptor that represents the read side of a pipe (rfd) is registered on the epoll instance.

       2. A pipe writer writes 2 kB of data on the write side of the pipe.

       3. A call to epoll_wait(2) is done that will return rfd as a ready file descriptor.

       4. The pipe reader reads 1 kB of data from rfd.

       5. A call to epoll_wait(2) is done.
       /*
       1. 表示管道读端的文件描述符(rfd)在epoll实例上注册。

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

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

       4. 管道读取器从rfd读取1 kB数据。

       5. 调用epoll_wait(2)完成。--->可能会产生无限期的阻塞 
       */

       If the rfd file descriptor has been added to the epoll interface using the EPOLLET (edge-triggered) flag, 
       the call to epoll_wait(2) done in step 5 will probably hang despite the available data still present in the file input buffer; 
       meanwhile the remote peer might be expecting a response based on the data it already sent.  
       The reason for this is that edge-triggered mode delivers events only when changes occur on the monitored file descriptor.  
       So, in step 5 the caller might end up waiting for some data that is already present inside the input buffer.  
       In the above example, an event on rfd will be generated because of the write done in 2 and the event is consumed in 3.  
       Since the read operation done in 4 does not consume the whole buffer data, the call to epoll_wait(2) done in step 5 might block indefinitely.
       /*
       如果使用EPOLLET(边缘触发)标志将rfd文件描述符添加到epoll接口,         
       在步骤5中完成的对epoll_wait(2)的调用可能会挂起,尽管文件输入缓冲区中仍然存在可用数据;         
       同时,远程对等端可能期待一个其已经发送的数据的响应。          

       这样做的原因是边缘触发模式仅在被监视的文件描述符发生变化时才传递事件。          

       因此,在步骤5中,调用者可能会等待输入缓冲区中已经存在的某些数据。          

       在上面的示例中,由于在步骤2中完成了写入,将在rfd上生成一个事件,并且该事件在3中使用。          

       由于在步骤4中完成的读取操作不会消耗整个缓冲区数据,所以在步骤5中完成的对epoll_wait(2)的调用可能会无限期地阻塞。
       */

       An application that employs the EPOLLET flag should use nonblocking file descriptors to avoid having a blocking read or write starve a task that is 
       handling multiple file descriptors. 
       The suggested way to use epoll as an edge-triggered (EPOLLET) interface is as follows:
       /*
       使用EPOLLET标志(边缘触发)的应用程序应该使用非阻塞文件描述符,以避免阻塞读或写使处理多个文件描述符的任务陷入饥饿。         
       使用epoll作为边缘触发(EPOLLET)接口的建议方法如下:
       */


              i   with nonblocking file descriptors; and

              ii  by waiting for an event only after read(2) or write(2) return EAGAIN.
        /*
              i 使用非阻塞文件描述符;以及

              ii 通过仅在read(2)或write(2)之后等待事件返回EAGAIN。
        */

       By contrast, when used as a level-triggered interface (the default, when EPOLLET is not specified), epoll is simply a faster poll(2), 
       and can be used wherever the latter is used since it shares the same semantics.
       /*
       相比之下,当用作级别触发接口时(默认情况下,当未指定EPOLLET时),epoll只是一个更快的轮询(2),        
       并且可以在使用后者的任何地方使用,因为它共享相同的语义。
       */

       Since even with edge-triggered epoll, multiple events can be generated upon receipt of multiple chunks of data, 
       the caller has the option to specify the EPOLLONESHOT flag,
        to tell epoll to disable the associated file descriptor after the receipt of an event with epoll_wait(2).  
        When the EPOLLONESHOT flag is specified, it is the caller's responsibility to rearm the file descriptor using epoll_ctl(2) with EPOLL_CTL_MOD.
        /*
        因为即使使用边缘触发epoll,在接收到多个数据块时也可以生成多个事件,         
        调用者具有指定EPOLLONESHOT标志的选项,         
        告诉epoll在接收到epoll_wait(2)事件后禁用相关的文件描述符。 

        当指定了EPOLLONESHOT标志时,调用者负责使用epoll_ctl(2)和EPOLL_CTL_MOD重新装备文件描述符。
        */

       If multiple threads (or processes, if child processes have inherited the epoll file descriptor across fork(2)) are blocked in epoll_wait(2) 
       waiting on the same epoll file descriptor and a file descriptor in the interest list that is marked for edge-triggered (EPOLLET) notification becomes ready, 
       just one of the threads (or processes) is awoken from epoll_wait(2).  
       This provides a useful optimization for avoiding "thundering herd" wake-ups in some scenarios.
       /*
       如果多个线程(或进程,如果子进程已经跨fork(2)继承了epoll文件描述符)在epoll_wait(2)中被阻塞等待相同的epoll文件描述符
       和兴趣列表中标记为边缘触发(EPOLLET)通知的文件描述符变为就绪,         
       只有一个线程(或进程)从epoll_wait(2)中被唤醒。          
       这提供了一个有用的优化,以避免在某些情况下“雷鸣般的羊群”唤醒。
       */

   Interaction with autosleep /*与autosleep的交互*/
       If the system is in autosleep mode via /sys/power/autosleep and an event happens which wakes the device from sleep, 
       the device driver will keep the device awake only until that event is queued.  
       To keep the device awake until the event has been processed, 
       it is necessary to use the epoll_ctl(2) EPOLLWAKEUP flag.

       /*
       如果系统通过/sys/power/autosleep处于自动休眠模式,并且发生了将设备从休眠状态唤醒的事件,         
       设备驱动程序将保持设备唤醒,直到该事件被排队。          
       为了保持设备唤醒直到事件被处理,         
       必须使用epoll_ctl(2)EPOLLWAKEUP标志。
       */

       When the EPOLLWAKEUP flag is set in the events field for a struct epoll_event, the system will be kept awake from the moment the event is queued, 
       through the epoll_wait(2) call which returns the event until the subsequent epoll_wait(2) call.  
       If the event should keep the system awake beyond that time, then a separate wake_lock should be taken before the second epoll_wait(2) call.
       /*
       当在结构epoll_event的events字段中设置EPOLLWAKEUP标志时,系统将从事件排队的那一刻起保持唤醒,         
       通过epoll_wait(2)调用返回事件,直到随后的epoll_wait(2)调用。          
       如果该事件应该使系统在超过该时间后保持唤醒,则应该在第二次epoll_wait(2)调用之前执行单独的wake_lock。
       */

   /proc interfaces
       The following interfaces can be used to limit the amount of kernel memory consumed by epoll:
       /*可以使用以下接口来限制epoll占用的内核内存量:*/

       /proc/sys/fs/epoll/max_user_watches (since Linux 2.6.28)
              This specifies a limit on the total number of file descriptors that a user can register across all epoll instances on the system.  
              The limit is per real user ID.  Each registered file descriptor costs roughly 90 bytes on a 32-bit kernel, and roughly 160 bytes on a 64-bit kernel.  
              Currently, the default value for max_user_watches is 1/25 (4%) of the available low memory, divided by the registration cost in bytes.
              /*
              这指定了用户可以在系统上的所有epoll实例中注册的文件描述符总数的限制。                 
              每个注册的文件描述符在32位内核上花费大约90个字节,在64位内核上花费大约160个字节。                 
              当前,max_user_watches的默认值是可用低内存的1/25(4%)除以注册成本(字节)。
              */

   Example for suggested usage
       While the usage of epoll when employed as a level-triggered interface does have the same semantics as poll(2), 
       the edge-triggered usage requires more clarification to avoid stalls in the application event loop.  
       In this example, listener is a nonblocking socket on which listen(2) has been called.  
       The function do_use_fd() uses the new ready file descriptor until EAGAIN is returned by either read(2) or write(2). 
        An event-driven state machine application should, after having received EAGAIN, 
        record its current state so that at the next call to do_use_fd() it will continue to read(2) or write(2) from where it stopped before.

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

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

           /* Code to set up listening socket, 'listen_sock',
              (socket(), bind(), listen()) omitted */

           epollfd = epoll_create1(0);
           if (epollfd == -1) {
               perror("epoll_create1");
               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_wait");
                   exit(EXIT_FAILURE);
               }

               for (n = 0; n < nfds; ++n) {
                   if (events[n].data.fd == listen_sock) {
                       conn_sock = accept(listen_sock,
                                          (struct sockaddr *) &addr, &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);
                   }
               }
           }

       When used as an edge-triggered interface, for performance reasons, 
       it is possible to add the file descriptor inside the epoll interface (EPOLL_CTL_ADD) once by specifying (EPOLLIN|EPOLLOUT).  
       This allows you to avoid continuously switching between EPOLLIN and EPOLLOUT calling epoll_ctl(2) with EPOLL_CTL_MOD.
        /*
        当用作边缘触发接口时,出于性能原因,通过指定(EPOLLIN|EPOLLOUT),可以在epoll接口(EPOLL_CTL_ADD)内添加一次文件描述符。          
        这可让您避免在EPOLLIN和EPOLLOUT之间不断切换,并使用EPOLL_CTL_MOD调用epoll_ctl(2)。
        */

   Questions and answers
       0.  What is the key used to distinguish the file descriptors registered in an interest list?

           The key is the combination of the file descriptor number and the open file description
            (also known as an "open file handle", the kernel's internal representation of an open file).

    /*
    0.  用于区分兴趣列表中注册的文件描述符的关键是什么?

        关键是文件描述符号和打开的文件描述的组合(称为“打开文件句柄”,内核对打开文件的内部表示)。
    */            

       1.  What happens if you register the same file descriptor on an epoll instance twice?

           You will probably get EEXIST.  However, it is possible to add a duplicate
            (dup(2), dup2(2), fcntl(2) F_DUPFD) file descriptor to the same epoll instance.  
            This can be a useful technique for filtering events, if the duplicate file descriptors are registered with different events masks.

    /* 
    1.  如果在epoll实例上注册相同的文件描述符两次会发生什么?

           你很可能会得到EEXIST。
           但是,可以添加重复的(dup(2),dup2(2),fcntl(2)F_DUPFD)文件描述符。               
           如果重复的文件描述符使用不同的事件掩码注册,则这对于过滤事件来说是一种有用的技术。
    */

       2.  Can two epoll instances wait for the same file descriptor?  If so, are events reported to both epoll file descriptors?

           Yes, and events would be reported to both.  However, careful programming may be needed to do this correctly.
    /*
    2.  两个epoll实例可以等待相同的文件描述符吗?  如果是,事件是否报告给两个epoll文件描述符?

           是的,事件将向双方报告。  但是,可能需要仔细编程才能正确执行此操作。
    */           

       3.  Is the epoll file descriptor itself poll/epoll/selectable?

           Yes.  If an epoll file descriptor has events waiting, then it will indicate as being readable.
    /*
    3.  epoll文件描述符本身是否为poll/epoll/selectable?

           是的  如果epoll文件描述符有等待的事件,则它将指示为可读。
    */           

       4.  What happens if one attempts to put an epoll file descriptor into its own file descriptor set?

           The epoll_ctl(2) call fails (EINVAL).  However, you can add an epoll file descriptor inside another epoll file descriptor set.
    /*
    4.  如果试图将epoll文件描述符放入自己的文件描述符集中会发生什么?

           epoll_ctl(2)调用失败(EINVAL)。  但是,您可以在另一个epoll文件描述符集中添加epoll文件描述符。
    */           

       5.  Can I send an epoll file descriptor over a UNIX domain socket to another process?

           Yes, but it does not make sense to do this, since the receiving process would not have copies of the file descriptors in the interest list.
    /*
    5.  我可以通过UNIX域套接字向另一个进程发送epoll文件描述符吗?

           是的,但是这样做没有意义,因为接收进程在兴趣列表中没有文件描述符的副本。
    */           

       6.  Will closing a file descriptor cause it to be removed from all epoll interest lists?

           Yes, but be aware of the following point.  A file descriptor is a reference to an open file description (see open(2)). 
            Whenever a file descriptor is duplicated via dup(2), dup2(2), fcntl(2) F_DUPFD, or fork(2),
             a new file descriptor referring to the same open file description is created. 
              An open file description continues to exist until all file descriptors referring to it have been closed.

           A file descriptor is removed from an interest list only after all the file descriptors referring to the underlying open file description have been closed.  
           This means that even after a file descriptor that is part of an interest list has been closed, 
           events may be reported for that file descriptor if other file descriptors referring to the same underlying file description remain open.  
           To prevent this happening, the file descriptor must be explicitly removed from the interest list (using epoll_ctl(2) EPOLL_CTL_DEL) before it is duplicated.  
           Alternatively, the application must ensure that all file descriptors are closed 
           (which may be difficult if file descriptors were duplicated behind the scenes by library functions that used dup(2) or fork(2)).
    /*
    6.  关闭一个文件描述符会导致它从所有epoll兴趣列表中删除吗?

           是的,但要注意以下几点。  
           文件描述符是对打开的文件描述的引用(参见open(2))。              
           每当通过dup(2)、dup2(2)、fcntl(2)F_DUPFD或fork(2)复制文件描述符时,              
           创建引用相同打开文件描述的新文件描述符。                
           打开的文件描述将继续存在,直到所有引用它的文件描述符都被关闭。

           只有在所有引用底层打开文件描述的文件描述符都已关闭后,才能从兴趣列表中删除文件描述符。              
           这意味着即使在作为兴趣列表的一部分的文件描述符被关闭之后,             
           如果引用相同底层文件描述的其它文件描述符保持打开,则可以报告该文件描述符的事件。              
           为了防止这种情况发生,在复制文件描述符之前,必须从兴趣列表中显式删除文件描述符(使用epoll_ctl(2)EPOLL_CTL_DEL)。              
           或者,应用程序必须确保关闭所有文件描述符             
           (如果文件描述符在后台被使用dup(2)或fork(2)的库函数复制,这可能会很困难)。
    */
       7.  If more than one event occurs between epoll_wait(2) calls, are they combined or reported separately?

           They will be combined.
    /*
    7.  如果在epoll_wait(2)调用之间发生了多个事件,它们是合并报告还是单独报告?

           他们将被合并。
    */           

       8.  Does an operation on a file descriptor affect the already collected but not yet reported events?

           You can do two operations on an existing file descriptor.  Remove would be meaningless for this case.  Modify will reread available I/O.
    /*
    8.  对文件描述符的操作是否会影响已收集但尚未报告的事件?

           您可以对现有的文件描述符执行两个操作。  
           删除操作对本案例毫无意义。  
           修改操作将重新读取可用I/O。
    */       

       9.  Do I need to continuously read/write a file descriptor until EAGAIN when using the EPOLLET flag (edge-triggered behavior)?

           Receiving an event from epoll_wait(2) should suggest to you that such file descriptor is ready for the requested I/O operation.  
           You must consider it ready until the next (nonblocking) read/write yields EAGAIN. 
            When and how you will use the file descriptor is entirely up to you.

           For packet/token-oriented files (e.g., datagram socket, terminal in canonical mode), 
           the only way to detect the end of the read/write I/O space is to continue to read/write until EAGAIN.

           For stream-oriented files (e.g., pipe, FIFO, stream socket), 
           the condition that the read/write I/O space is exhausted can also be detected by checking the amount of data read from / written to the target file descriptor.  
           For example, if you call read(2) by asking to read a certain amount of data and read(2) returns a lower number of bytes,
            you can be sure of having exhausted the read I/O space for the file descriptor.  The same is true when writing using write(2).  
            (Avoid this latter technique if you cannot guarantee that the monitored file descriptor always refers to a stream-oriented file.)

    /*
    9.  当使用EPOLLET标志(边缘触发行为)时,我需要连续读/写文件描述符直到EAGAIN吗?

           从epoll_wait(2)接收事件应该向您表明这样的文件描述符已经为请求的I/O操作做好了准备。             
            在下一次(非阻塞)读/写产生EAGAIN之前,您必须将其视为做好了准备。              
            何时以及如何使用文件描述符完全取决于您。

           对于面向分组/令牌的文件(例如, 数据报套接字,规范模式下的终端),             
           检测 读/写 I/O空间结束的唯一方法是继续 读/写 直到EAGAIN。

           对于面向流的文件(例如, 管道、FIFO、流套接字),             
           读/写 I/O空间 耗尽的情况也可以通过检查从目标文件描述符 读取/写入 目标文件描述符的数据量来检测。              
           例如,如果通过请求读取确定量的数据来调用read(2),而read(2)返回较少量的字节数,             
           你可以肯定已经用尽了文件描述符的读I/O空间。  
           当使用write(2)写入时也是如此。               
           (如果不能保证被监视的文件描述符始终引用面向流的文件,请避免使用后一种技术。)
    */

   Possible pitfalls and ways to avoid them 
   /*可能的陷阱和避免它们的方法*/
       o Starvation (edge-triggered)

       If there is a large amount of I/O space, it is possible that by trying to drain it the other files will not get processed causing starvation. 
        (This problem is not specific to epoll.)

       The solution is to maintain a ready list and mark the file descriptor as ready in its associated data structure, 
       thereby allowing the application to remember which files need to be processed but still round robin amongst all the ready files.  
       This also supports ignoring subsequent events you receive for file descriptors that are already ready.

    /*
    饥饿(边缘触发)

       如果有大量的I/O空间,那么通过尝试耗尽它,其他文件可能无法得到处理,从而导致饥饿。   
       (这个问题并不是epoll特有的。)

       解决方案是维护做好了准备的(就绪)列表并在其相关数据结构中将文件描述符标记为就绪,         
       从而允许应用记住哪些文件需要被处理,但仍然在所有准备好的文件中循环。          
       这也支持忽略您收到的已经准备好的文件描述符的后续事件。
    */

       o If using an event cache...

       If you use an event cache or store all the file descriptors returned from epoll_wait(2), 
       then make sure to provide a way to mark its closure dynamically (i.e., caused by a previous event's processing).  
       Suppose you receive 100 events from epoll_wait(2), and in event #47 a condition causes event #13 to be closed.  
       If you remove the structure and close(2) the file descriptor for event #13, 
       then your event cache might still say there are events waiting for that file descriptor causing confusion.

       One solution for this is to call, 
       during the processing of event 47, epoll_ctl(EPOLL_CTL_DEL) to delete file descriptor 13 and close(2), 
       then mark its associated data structure as removed and link it to a cleanup list.  
       If you find another event for file descriptor 13 in your batch processing, 
       you will discover the file descriptor had been previously removed and there will be no confusion.

    /*
    o如果使用事件缓存.

       如果从epoll_wait(2)返回的所有文件描述符使用事件缓存或存储,         
       则确保提供了一种动态地标记其关闭的方式(即,由先前事件的处理引起)。          
       假设从epoll_wait(2)接收到100个事件,在事件#47中,一个条件导致事件#13关闭。         
       如果你删除结构并close(2)事件#13的文件描述符,         
       那么你的事件缓存可能仍然会说有事件在等待那个文件描述符,从而导致混乱。

       一个解决办法是调用,        
        在事件47的处理期间,epoll_ctl(EPOLL_CTL_DEL)删除文件描述符13并close(2),         
        然后将其关联的数据结构标记为已删除,并将其关联到清除列表。          
        如果在批处理中发现文件描述符13的另一个事件,         
        你会发现文件描述符已经被删除,不会有任何混乱。
    */   

VERSIONS
       The epoll API was introduced in Linux kernel 2.5.44.  Support was added to glibc in version 2.3.2.

CONFORMING TO
       The epoll API is Linux-specific.  Some other systems provide similar mechanisms, for example, FreeBSD has kqueue, and Solaris has /dev/poll.

NOTES
       The set of file descriptors that is being monitored via an epoll file descriptor 
       can be viewed via the entry for the epoll file descriptor in the process's /proc/[pid]/fdinfo directory.  
       See proc(5) for further details.

       The kcmp(2) KCMP_EPOLL_TFD operation can be used to test whether a file descriptor is present in an epoll instance.

SEE ALSO
       epoll_create(2), epoll_create1(2), epoll_ctl(2), epoll_wait(2), poll(2), select(2)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  
       A description of the project, information about reporting bugs, and the latest version of this page, can be found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        2019-03-06                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     EPOLL(7)
~
~