epoll for IO multiplexing

Posted by ilikemath2002 on Mon, 20 Dec 2021 15:49:06 +0100

select and poll are used in similar ways, with obvious disadvantages. Epoll is a unique IO reuse function of Linux, which is very different from the first two. The working principle of epoll can be summarized as three functions and two modes. The third function is epoll_create(), epoll_ctl(),epoll_wait(). The two modes are ET and LT

epoll_create()

int  epoll_create(int size);
Create a epoll Handle to, size It is used to tell the kernel the total number of listeners.
call epoll_create()Build a epoll object(stay epoll Allocate resources for this handle object in the file system)
When a process calls epoll_create Method, Linux The kernel creates a eventpoll structural morphology

epoll_ctl()

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll Event registration function, which is different from select()When listening for events( epoll use epoll_wait Listen) tell the kernel what to listen for
 Instead, register the event type to listen to here. The first parameter is epoll_create()The second parameter represents the return value of
 As, it is represented by three macros:
EPOLL_CTL_ADD: Register new fd reach epfd Medium;
EPOLL_CTL_MOD: Modify registered fd Listening events of;
EPOLL_CTL_DEL: from epfd Delete one from fd;
The third parameter needs to be monitored fd,The fourth parameter tells the kernel what to listen for. The corresponding structure is as follows
struct epoll_event {
    __uint32_t events; /* Epoll events */
    epoll_data_t data; /* User data variable */
};
among events Member describes the event type. epoll Supported event types and poll Basically the same. express epoll Event type macros are poll Corresponding
 Add before macro“ E",such as epoll The data readable event for is EPOLLIN. but epoll There are two additional event types--EPOLLET 
and EPOLLONESHOT.They are for epoll Efficient operation is critical. data Members are used to store user data, and their types epoll_data_t Determination of
 The meaning is as follows:
typedef union epoll_data {
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

epoll_wait()

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
Wait for the event to occur, similar to select()Call.

The first parameter represents the file descriptor of the event table in the kernel;

The parameter events is used to get a collection of events from the kernel. If an event is detected, all ready events are copied from the kernel event table to the array it points to, which is different from select and poll;

Maxevents tells the kernel how big the events are and how many events to listen to at most. The value of maxevents cannot be greater than the value of epoll created_ size at create();
The parameter timeout is the timeout (milliseconds, 0 will be returned immediately, and - 1 is blocking).
This function returns the number of events to be processed. For example, returning 0 means timeout.

LT

LT(level triggered)Is the default working mode, and supports block and no-block socket. 
The kernel tells you whether a file descriptor is ready, and then you can check the ready file descriptor fd conduct IO Operation.
If nothing is done, the kernel will continue to notify, so there is less possibility of programming errors in this mode.
conventional select/poll Are representatives of this model.

ET

ET (edge-triggered)It is a high-speed working mode and only supports no-block socket. 
In this mode, when the descriptor is never ready to become ready, the kernel passes epoll I tell you, and I won't send more for that file descriptor
 Thread notification until some operation is done so that the file descriptor is no longer ready(For example, send, receive or receive requests, or send and receive requests
 An error occurs when the received data is less than a certain amount EWOULDBLOCK Error).
If you've been wrong about this fd do IO operation(This causes it to become not ready again),The kernel will not send more notifications(only once). 

epoll features

(1) It supports a process to open a large number of socket descriptors (FD). The upper limit of FD it supports is the maximum number of files that can be opened. This number is about 100000 on a machine with 1GB of memory. The specific number can be viewed in cat / proc / sys / FS / file max. generally speaking, this number has a great relationship with the system memory.

(2) IO efficiency does not decrease linearly with the increase of FD number. Another fatal weakness of traditional select/poll is that when you have a large set of sockets, only some sockets are "active" at any time, but each call of select/poll will linearly scan all sets, resulting in a linear decrease in efficiency.

However, epoll does not have this problem. It only operates on "active" sockets - this is because in the kernel implementation, epoll is implemented according to the callback function above each fd. Only "active" sockets will actively call the callback function, while other idle state sockets will not.

In some cases, if all socket s are basically active - such as a high-speed LAN environment, epoll is no more efficient than select/poll; On the contrary, the efficiency may decrease slightly.

(3) Use mmap to speed up the message transmission between kernel and user space. This actually involves the specific implementation of epoll. Whether it is select,poll or epoll, the kernel needs to notify FD messages to user space. How to avoid unnecessary memory copies is very important. In this regard, epoll is realized through the same memory as mmap in kernel and user space.

Use example

#include <sys/epoll.h>
#include <stdlib.h>
#include <error.h>
#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    //Step 1: bring the file descriptor into epoll supervision before epoll starts working
    struct epoll_event  listen_fd;
    listen_fd.data.fd=0;
    listen_fd.events=EPOLLIN;    
    int epoll_fd=epoll_create(10);
    epoll_ctl(epoll_fd,EPOLL_CTL_ADD,0,&listen_fd);

    //Step 2: epoll starts to work. The blocked waiting file descriptor is ready
    struct epoll_event ready_events[10];
    int ret=epoll_wait(epoll_fd,ready_events,10,-1);
    
    //Step 3: epoll completes the work to see whether the socket of interest is ready
    if(ret<0)
    { 
            cout<<"epoll error"<<endl; return -1;
    }
    else if(ret==0)
        {cout<<"spoll time out";return -1;}
    else
    {
        for(int i=0;i<ret;i++)
        {
            if(ready_events[i].data.fd==0)
            {
                char  tmp[10];
                read(0,tmp,10);
                cout<<tmp<<endl;
            }
        }

    }
}

 

Topics: Linux epoll