EPOLL-I/O Event Notification Facility
In Linux network programming, it is a long time to use Select to do event triggering. In the new Linux kernel, there is a mechanism to replace it, which is epoll.
The biggest advantage over Select,epoll is that it does not reduce efficiency as the number of listening FD increases. Because in the select implementation in the kernel, it is handled by polling, the more the number of polled FD, the more natural time consuming. Also, the LINUX/POSIX_TYPES.H header file has such a statement:
#define __FD_SETSIZE 1024
Indicates that the select has up to 1024 FD at the same time, of course, you can enlarge the number by modifying the header file and then recompiling the kernel, but this does not seem to be a permanent fix.
The Epoll interface is very simple, with a total of three functions:
1. int epoll_create (int size);
Creates a epoll handle, which is used to tell the kernel how large the number of listeners is. This parameter differs from the first parameter in select () and gives the value of the maximum listener fd+1. Note that when you create a good epoll handle, it will occupy an FD value, under Linux if you look at the/proc/process id/fd/, you can see this fd, so after using Epoll, you must call Close () closed, otherwise it may cause FD to be depleted.
2. int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event);
The Epoll event registration function, which differs from select () is to tell the kernel what type of event to listen to when listening to an event, but to register the type of event to listen for first. The first parameter is the return value of Epoll_create (), and the second parameter represents the action, which is represented by three macros:
Epoll_ctl_add: Register the new FD to EPFD;
Epoll_ctl_mod: Modify the Listening event of the registered FD;
Epoll_ctl_del: Deletes an FD from the EPFD;
The third parameter is the FD that needs to be monitored, and the fourth parameter tells the kernel what to listen to, and the struct epoll_event structure is as follows:
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 can be a collection of several macros:
Epollin: Indicates that the corresponding file descriptor can be read (including closing the socket properly);
Epollout: Indicates that the corresponding file descriptor can be written;
Epollpri: Indicates that the corresponding file descriptor has an urgent data readable (here should indicate the arrival of Out-of-band data);
Epollerr: Indicates that the corresponding file descriptor has an error;
Epollhup: Indicates that the corresponding file descriptor is hung;
Epollet: Sets the Epoll as the Edge trigger (edge triggered) mode, which is relative to the level triggered.
Epolloneshot: Listen to only one event, when listening to the event, if you still need to continue to listen to this socket, you need to add this socket to the Epoll queue
3. int epoll_wait (int epfd, struct epoll_event * events, int maxevents, int timeout);
Wait for the event to occur, similar to the Select () call. Parameter events are used to get the collection of events from the kernel, maxevents the kernel of this event, the Maxevents value cannot be greater than the size when the Epoll_create () is created, and the parameter timeout is the timeout (milliseconds, 0 will return immediately ,-1 will be uncertain, and there are claims that it is permanently blocked. The function returns the number of events that need to be handled, such as returning 0 to indicate that the timeout has expired.
4, about ET, lt two kinds of working mode:
You can draw the conclusion that:
The ET mode is only notified when the state changes, and the so-called state changes do not include the data in the buffer, which means that if you want to use the ET mode, you need to keep read/write until the error occurs. Many people reflect why the use of ET mode only received a part of the data will no longer be notified, mostly because of this, and the LT mode is as long as there is no data processing will always be informed.
So how exactly do you use Epoll? actually very simple.
By including a header file #include <sys/epoll.h> and a few simple APIs will be able to greatly improve your network server support number.
First, you create a epoll handle by create_epoll (int Maxfds), where Maxfds is the maximum number of handles that Epoll supports. This function returns a new Epoll handle, and all subsequent operations are performed through this handle. When you are finished, use close () to turn off the created Epoll handle.
Then in your network main loop, each frame of the call epoll_wait (int epfd, epoll_event events, int max events, int timeout) to query all network interfaces, see which one can read, which one can write. The basic syntax is:
Nfds = epoll_wait (KDPFD, events, maxevents,-1);
Where KDPFD is the handle that is created with epoll_create, events is a epoll_event* pointer, and when epoll_wait this function succeeds, all of the read and write events are stored in epoll_events. Max_events is the number of all socket handles that are currently listening. The last timeout is the epoll_wait timeout, which means that when 0 is returned immediately, the 1 indicates that it has been waiting until there is an event range, for any positive integer to represent such a long time, if there has been no event, then the scope. Generally if the network main loop is a separate thread, you can use-one, etc., so as to ensure some efficiency, if it is the same thread with the main logic, you can use 0来 to ensure the efficiency of the main cycle.
The epoll_wait range should be followed by a loop that benefits all events.
Almost all Epoll programs use the following framework:
for (;;)
{
Nfds = epoll_wait (epfd,events,20,500);
for (I=0;i<nfds;++i)
{
if (EVENTS[I].DATA.FD==LISTENFD)//There is a new connection
{
CONNFD = Accept (LISTENFD, (SOCKADDR *) &clientaddr, &clilen); Accept this connection
EV.DATA.FD=CONNFD;
ev.events=epollin| Epollet;
Epoll_ctl (Epfd,epoll_ctl_add,connfd,&ev); Add new FD to Epoll's listening queue
}
else if (Events[i].events&epollin)//received data, read socket
{
n = Read (SOCKFD, line, Maxline)) < 0/
EV.DATA.PTR = MD; MD for custom type, adding data
Ev.events=epollout| Epollet;
Epoll_ctl (Epfd,epoll_ctl_mod,sockfd,&ev);//modify identifiers, wait for the next loop to send data, the essence of asynchronous processing
}
else if (events[i].events&epollout)//have data to be sent, write socket
{
struct myepoll_data* MD = (myepoll_data*) events[i].data.ptr; Fetch data
SOCKFD = md->fd;
Send (SOCKFD, Md->ptr, strlen ((char*) md->ptr), 0); Send data
EV.DATA.FD=SOCKFD;
ev.events=epollin| Epollet;
Epoll_ctl (Epfd,epoll_ctl_mod,sockfd,&ev); modifying identifiers, waiting for the next loop to receive data
}
Else
{
Other processing
}
}
}
A complete server-side example is given below:
#include <iostream> #include <sys/socket.h> #include <sys/epoll.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <errno.h>
using namespace Std;
#define Maxline 5 #define OPEN_MAX 100 #define LISTENQ 20 #define SERV_PORT 5000 #define INFTIM 1000
void setnonblocking (int sock) { int opts; Opts=fcntl (SOCK,F_GETFL); if (opts<0) { Perror ("Fcntl (SOCK,GETFL)"); Exit (1); } opts = opts| O_nonblock; if (Fcntl (sock,f_setfl,opts) <0) { Perror ("Fcntl (sock,setfl,opts)"); Exit (1); } }
int main (int argc, char* argv[]) { int I, Maxi, LISTENFD, CONNFD, sockfd,epfd,nfds< |