Reproduced from: http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html
Definition:
Epoll is the Linux kernel to handle a large number of handles to improve the poll, is the Linux multiplexing IO interface select/poll enhanced version, it can significantly reduce the program in a large number of concurrent connections in the case of only a small amount of active system CPU utilization. Because it uses a collection of file descriptors to deliver results instead of forcing the developer to prepare a collection of file descriptors to be listened to every time it waits for an event, another reason is that when capturing an event, it does not have to traverse the entire set of listeners that are listening. Just iterate through the descriptor collections that are joined to the ready queue by asynchronous wake-up of kernel IO events. In addition to providing a horizontal trigger (level triggered) for select\poll that IO event, Epoll provides edge triggering (edge triggered), which makes it possible for user-space programs to cache IO status and reduce epoll_wait/ Epoll_pwait calls that provide the efficiency of the application.
Working mode:
LT (level triggered): Horizontal trigger, by default, both block and No-block sockets are supported, in which case the kernel tells us if a file descriptor is ready and, if it is, you can io the ready fd. If you do not do anything, the kernel will continue to notify you, so this mode of programming error is less likely. Traditional Select\poll are representative of this model.
ET (edge-triggered): Edge trigger, high-speed working mode, only support no-block socket. In this mode, the kernel tells you through Epoll when the descriptor is never ready to become ready. Then it assumes that you know that the file descriptor is ready and that no more ready notifications are sent for that descriptor until you do something that causes that file descriptor to be no longer in the ready state (for example: you are sending, receiving, or accepting requests, Or a ewouldblock error is caused by sending less than a certain number of accepted data. Note, however, that the kernel does not send more notifications if it does not always do IO operations on this FS (which causes it to become not ready again).
Difference : LT event will not discard, but only read the buffer inside the data can allow users to read, you are constantly informed. ET is only notified when the event occurs.
How to use:
1, int epoll_create (int size)
Create a epoll handle with the parameter size to tell the kernel the number of listeners.
2, int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event)
Epoll Event Registration function,
The parameter EPFD is a epoll handle;
The parameter op represents the action, which is represented by 3 macros: Epoll_ctl_add (registering the new FD to EPFD), Epoll_ctl_mod (modifying the listening event of the registered FD), Epoll_ctl_del (Removing an FD from EPFD);
The parameter fd is the identifier that needs to be monitored;
Parameter event tells the kernel what to listen to, and the event is structured as follows:
struct Epoll_event {
__uint32_t events; /* Epoll Events * *
epoll_data_t data; /* User Data variable *
/};
Where events can use a collection of the following several macros:
Epollin: Indicates that the corresponding file descriptor can be read (including normal shutdown of the end socket)
Epollout: Indicates that the corresponding file descriptor can be written
Epollpri: Indicates that the corresponding file descriptor has an urgent data readable (this 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 horizontal trigger (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.
Application Examples:
Next, I quote a simple program written by someone else in Google code to illustrate it. SVN path: http://sechat.googlecode.com/svn/trunk/
The program a simple chat room program, written in Linux C + +, the server is mainly implemented with the Epoll model, support for high concurrency, I tested in 10,000 clients connected to the server, server processing time less than 1 seconds, of course, the client is only connected with the server, Accept the server's welcome message only, and did not do other communication. Although the program is relatively simple, but when we consider the server high concurrency also provides a way of thinking. In this program, I have all the debugging information and some information unrelated to the Epoll, and add the necessary comments, should be easy to understand.
The program contains a total of 2 header files and 3 CPP files. Of these 3 CPP files, each CPP file is an application, Server.cpp: Server program, Client.cpp: A single client program, Tester.cpp: Simulate high concurrency, open 10,000 clients to connect to the server.
Utils.h header file, which contains a set socket as not blocking function, as follows:
int setnonblocking (int sockfd)
{
CHK (fcntl sockfd, F_SETFL, Fcntl (SOCKFD, F_GETFD, 0) | O_nonblock));
return 0;
}
Local.h header file, some constant definitions and function declarations, as follows:
#define BUF_SIZE 1024//default buffer #define SERVER_PORT 44444//Listener Port #define SERVER_HOST "192.168.34 .15 "//server IP address #define EPOLL_RUN_TIMEOUT-1//epoll Timeout #define The maximum of epoll_size 10000//epoll listener clients Number #define Str_welcome "WELCOME to sechat! Your ID is:client #%d "#define Str_message" Client #%d>>%s "#define Str_noone_connected" Noone CONNECTED to server Except you! "#define CMD_EXIT" exit//Two useful macro definitions: Check and assign values and detect #define CHK (eval) if (eval < 0) {perror ("eval"); EXIT ( -1);} #d
Efine CHK2 (res, eval) if ((res = eval) < 0) {perror ("eval"); exit (-1);} ================================================================================================//Function name: Setnonblocking//Function Description: Set socket to not block//input: [in] SOCKFD socket identifier//output: No//return: 0//========================================================================================= ======= int setnonblocking (iNT SOCKFD); ================================================================================================//Function name: Handle_message//Function Description: Handle each client socket//input: [in] NEW_FD socket identifier//output: No//return: Returns the length of data received from the client//=========================================================================== ===================== int handle_message (int new_fd);
Server.cpp file, the Epoll model is implemented here as follows:
#include "local.h" #include "utils.h" using namespace std;
The list list<int> clients_list that holds the client socket descriptor; int main (int argc, char *argv[]) {int listener;
Monitor socket struct sockaddr_in addr, their_addr;
addr.sin_family = pf_inet;
Addr.sin_port = htons (Server_port);
ADDR.SIN_ADDR.S_ADDR = inet_addr (server_host);
Socklen_t Socklen;
Socklen = sizeof (struct sockaddr_in);
static struct epoll_event ev, events[epoll_size]; ev.events = Epollin | Epollet;
Interested in reading, Edge triggering char message[buf_size]; int EPFD; Epoll descriptor clock_t Tstart;
Calculation program run time int client, res, epoll_events_count; CHK2 (Listener, Socket (pf_inet, sock_stream, 0)); Initialize the listening socket setnonblocking (listener); Set the listening socket to not block CHK (BIND (listener, (struct sockaddr *) &addr, sizeof (addr))); Binding listens to the socket CHK (Listen (listener, 1)); Set up the Listening CHK2 (epfd,epoll_create epoll_siZE));
Create a Epoll descriptor and add the listening socket to epoll EV.DATA.FD = listener;
CHK (Epoll_ctl (EPFD, Epoll_ctl_add, Listener, &ev));
while (1) {CHK2 (epoll_events_count,epoll_wait (EPFD, events, Epoll_size, epoll_run_timeout));
Tstart = Clock (); for (int i = 0; i < Epoll_events_count i++) {if (EVENTS[I].DATA.FD = = listener) The new connection arrives, adds the connection to the Epoll, and sends the welcome message {CHK2 (listener, struct sockaddr *) &their_a
DDR, &socklen));
setnonblocking (client);
EV.DATA.FD = client;
CHK (Epoll_ctl (EPFD, Epoll_ctl_add, client, &ev)); Clients_list.push_back (client);
Add new client to list bzero (message, buf_size);
res = sprintf (message, str_welcome, client);
CHK2 (res, send (client, message, Buf_size, 0));
}else { CHK2 (Res,handle_message (EVENTS[I].DATA.FD)); Note: There is no call to epoll_ctl the event type of the socket, but you can continue to receive messages sent by the client} printf ("Statistics:%d events Han
Dled at:%.2f second (s) \ n ", Epoll_events_count, (double) (Clock ()-Tstart)/clocks_per_sec);
Close (listener);
Close (EPFD);
return 0;
int handle_message (int client) {char buf[buf_size], message[buf_size];
Bzero (buf, buf_size);
Bzero (message, buf_size);
int Len; CHK2 (LEN,RECV (client, buf, buf_size, 0));
Accept client Information if (len = = 0)//client closes or fails, closes the socket and removes the socket {CHK (Close (client)) from the list;
Clients_list.remove (client); else//Send message to client {if (clients_list.size () = = 1) {CHK (client, Str_no
one_connected, strlen (str_noone_connected), 0));
return Len;
} sprintf (Message, str_message, client, buf);
List<int>::iterator it; for (it = clients_list.Begin (); It!= clients_list.end ();
it++) {if (*it!= client) {CHK (send (*it, message, buf_size, 0));
}} return len; }
Tester.cpp files, analog server high concurrency, open 10,000 clients to connect to the server, as follows:
#include "local.h" #include "utils.h" using namespace std; Char Message[buf_size]; Accept server information list<int> list_of_clients;
Store all clients int res;
clock_t Tstart;
int main (int argc, char *argv[]) {int sock;
struct sockaddr_in addr;
addr.sin_family = pf_inet;
Addr.sin_port = htons (Server_port);
ADDR.SIN_ADDR.S_ADDR = inet_addr (server_host);
Tstart = Clock (); for (int i=0; i<epoll_size; i++)//Generate Epoll_size client, here are 10,000, simulate high concurrency {CHK2 sock,socket (pf_inet, Sock_stream
, 0));
CHK (Connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0);
List_of_clients.push_back (sock);
Bzero (&message, buf_size);
CHK2 (RES,RECV (sock, message, buf_size, 0));
printf ("%s\n", message); } List<int>::iterator it;
Remove all client for (it = List_of_clients.begin (), it!= list_of_clients.end (); it++) Close (*it); printf ("Test passed at:%.2f second (s) \ n", (double) (Clock ()-Tstart)/clocks_per_sec);
printf ("Total server connections is:%d\n", epoll_size);
return 0; }
I do not give a screenshot of the execution results of the program, but the following screenshot is the code author's own test, you can see that concurrent 10000 without pressure AH
A single client connects to the server, client.cpp files, as follows:
#include "local.h" #include "utils.h" using namespace std;
Char Message[buf_size]; /* Process: Call fork to generate two processes, two processes through the pipeline communication subprocess: Waiting for customer input, and the customer entered the information through the pipeline to the parent process parent process: Accept server information and display, send information received from child process to service
Service/int main (int argc, char *argv[]) {int sock, PID, pipe_fd[2], EPFD;
struct sockaddr_in addr;
addr.sin_family = pf_inet;
Addr.sin_port = htons (Server_port);
ADDR.SIN_ADDR.S_ADDR = inet_addr (server_host);
static struct epoll_event ev, events[2]; ev.events = Epollin |
Epollet;
Exit flag int continue_to_work = 1;
CHK2 (Sock,socket (pf_inet, Sock_stream, 0));
CHK (Connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0);
CHK (pipe (PIPE_FD));
CHK2 (Epfd,epoll_create (epoll_size));
EV.DATA.FD = sock;
CHK (Epoll_ctl (EPFD, Epoll_ctl_add, Sock, &ev));
EV.DATA.FD = pipe_fd[0];
CHK (Epoll_ctl (EPFD, Epoll_ctl_add, pipe_fd[0], &ev));
Call fork generates two process CHK2 (Pid,fork ());
Switch (PID) { Case 0://child process close (pipe_fd[0]);
Turn off read-side printf ("Enter" Exit ' to exit\n ');
while (continue_to_work) {bzero (&message, buf_size);
Fgets (Message, buf_size, stdin);
When the Exit command is received, exit if (strncasecmp (message, Cmd_exit, strlen (cmd_exit)) = = 0) {
continue_to_work = 0; else {CHK (write pipe_fd[1], message, strlen (message)-
1));
}} break; Default://Parent process close (pipe_fd[1]);
Close write end int epoll_events_count, RES; while (continue_to_work) {CHK2 epoll_events_count,epoll_wait (EPFD, events, 2, Epoll_run_timeo
UT)); for (int i = 0; i < Epoll_events_count i++) {BzerO (&message, buf_size); if (events[i].data.fd = = sock)//receive information from the server {CHK2 (RES,RECV (sock, message, buf_
SIZE, 0));
if (res = = 0)//server has closed {CHK (Close (sock));
continue_to_work = 0;
else {printf ("%s\n", message);
' Else//accept information from child process {
CHK2 (res, read (EVENTS[I].DATA.FD, message, buf_size));
if (res = = 0) {continue_to_work = 0; else {CHK (send (sock, message, buf_size, 0))
;
}
} }} if (PID) {close (pipe_fd[0]);
Close (sock);
}else {Close (pipe_fd[1]);
return 0; }