Original address: http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html
Defined:
The Epoll is an enhanced version of the Linux kernel for processing large numbers of handles and is a poll of the Linux Select/poll IO interface, which significantly reduces the CPU utilization of the program in cases where there is only a small amount of activity in a large number of concurrent connections. Because it will reuse the file descriptor collection to pass the result instead of forcing the developer to re-prepare the set of file descriptors to be listened to each time it waits for the event, another reason is that it does not have to traverse the entire set of descriptors that are being listened to when the event is fetched. Just walk through the set of descriptors that are joined to the ready queue by a kernel IO event that wakes up asynchronously. Epoll provides edge triggering (edge triggered) in addition to the level triggered of Select\poll IO events, which makes it possible for user space programs to cache IO status and reduce epoll_wait/ The epoll_pwait call, which provides the efficiency of the application.
Working mode:
LT (level triggered): Horizontal trigger, default mode, support both block and no-block socket, in this practice, the kernel tells us whether a file descriptor is ready, if ready, you can do the IO operation of this 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 the representatives of this model.
ET (edge-triggered): Edge trigger, high-speed mode of operation, only support no-block socket. In this mode, when the descriptor is never ready to become ready, the kernel tells you through Epoll. It then assumes that you know that the file descriptor is ready and no more ready notifications are sent for that descriptor until you do something that causes the file descriptor to no longer be ready (for example, you are sending, accepting, or accepting requests, Or a ewouldblock error is caused by sending the accepted data less than a certain amount). Note, however, that the kernel does not send more notifications if the FS does not do IO operations (causing it to become not ready again).
Difference : LT event will not be discarded, but as long as the read buffer inside the data can let the user read, then constantly notify you. 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 how many to listen.
2, int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event)
Epoll Event Registration function,
The parameter EPFD is the handle of epoll;
The parameter op represents the action, represented by 3 macros: Epoll_ctl_add (Register the new FD to EPFD), Epoll_ctl_mod (Modify the listener event of the registered FD), Epoll_ctl_del (delete a fd from EPFD);
The parameter fd is the identifier that needs to be monitored;
The parameter event tells the kernel what to listen for, the structure of the event is as follows:
struct Epoll_event { __uint32_t events; /* Epoll Events */ epoll_data_t data; /* USER Data variable */};
Where events can be set up with the following several macros:
Epollin: Indicates that the corresponding file descriptor can be read (including a graceful shutdown of the peer 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 that there is out-of-band data coming)
Epollerr: Indicates an error occurred for the corresponding file descriptor
Epollhup: Indicates that the corresponding file descriptor is hung up;
Epollet: Set Epoll to edge triggered mode, which is relative to the horizontal trigger (level triggered)
Epolloneshot: Listen to only one event, when the event is monitored, if you still need to continue to listen to the socket, you need to add the socket to the Epoll queue again
3, int epoll_wait (int epfd, struct epoll_event * events, int maxevents, int timeout)
Waits for the event to occur, similar to a select () call. Parameter events is used to get the collection of event from the kernel, the maxevents tells the kernel how big the events are, the value of this maxevents cannot be greater than the size when the Epoll_create () was created, the parameter timeout is the timeout (in milliseconds, 0 returns immediately , 1 will be uncertain, and there are statements that are permanently blocked). The function returns the number of events that need to be processed, such as returning 0 to indicate a timeout.
Application Examples:
Below, I refer to 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 high concurrency, I test when there are 10,000 client Connection server, the server processing time of less than 1 seconds, of course, the client is only connected with the server, Accepts the server's welcome message, and does not do any other communication. Although the program is relatively simple, we also provide a way of thinking when we consider the high concurrency of the server. In this program, I have taken all the debugging information and some information unrelated to Epoll, and add the necessary comments, it should be easy to understand.
The program contains a total of 2 header files and 3 CPP files. Of the 3 CPP files, each CPP file is an application, Server.cpp: Server program, Client.cpp: Single client program, Tester.cpp: Simulate high concurrency, turn on 10,000 clients to connect to the server.
The Utils.h header file contains a set socket as a non-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 files, definitions of some constants and declarations of functions are as follows:
#define BUF_SIZE 1024//default buffer # Server_port 44444//Listening Port # define Server_host "192.168.34.1 5 "//server IP address # define EPOLL_RUN_TIMEOUT-1//epoll Timeout time # epoll_size 10000//epoll The maximum number of clients to monitor #d Efine str_welcome "WELCOME to sechat! You ID is:client #%d "#define Str_message" Client #%d>>%s "#define Str_noone_connected" noone CONNECTED to server ex Cept you! " #define CMD_EXIT "Exit"//Two useful macro definitions: Check and assign and detect # define CHK (eval) if (eval < 0) {perror ("eval"); EXIT (-1);} #define 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 designator//output: none/ /return: 0//=============================================================================================== =int setnonblocking (int sockfd);
================================================================================================//Function Name: handle_message//function Description: Process each client socket//input: [in] NEW_FD socket identifier//output: None 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;//store the client socket descriptor listlist<int> Clients_list;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; Calculate program run time int client, res, epoll_events_count; CHK2 (Listener, Socket (pf_inet, sock_stream, 0)); Initialize the listener socket setnonblocking (listener); Set the listener socket to not block CHK (BIND (listener, (struct sockaddr *) &addr, sizeof (addr))); Bind monitor Socket CHK (Listen (listener, 1)); Set up monitoring CHK2 (Epfd,epoll_create (epoll_size)); InvasiveA Epoll descriptor is built, and the listener socket is added 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)/ /New connection arrives, add connection to Epoll, and send welcome message {CHK2 (client,accept (Listener, (struct sockaddr *) &their_addr, &socklen)); setnonblocking (client); EV.DATA.FD = client; CHK (Epoll_ctl (EPFD, Epoll_ctl_add, client, &ev)); Clients_list.push_back (client); Add a 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: here andThere is no event type that calls Epoll_ctl to reset the socket, but still can continue to receive messages sent by the client}} printf ("Statistics:%d events handled 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 off or error, close socket, and remove socket {CHK (Close client) from list; Clients_list.remove (client); } else//Send message to client {if (clients_list.size () = = 1) {CHK (Send (client, Str_noone_c onnected, 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 file, simulate the high concurrency of the server, 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, analog 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 was:%d\n ", epoll_size); return 0;}
I will not give the results of the implementation of the program, but the following is the code author himself test, you can see, concurrency 10000 no pressure AH
A single client goes to connect to the server, client.cpp file, as follows:
#include "local.h" #include "utils.h" using namespace Std;char message[buf_size];/* process: Call fork to generate two processes, two processes to communicate through the pipeline Subprocess: Waits for customer input and sends the information entered by the customer through the pipeline to the parent process parent process: Accepts the server's information and displays it, sends the information received from the child process to the server */int main (int argc, char *argv[]) {int so CK, 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 to generate two process CHK2 (Pid,fork ()); Switch (PID) {case 0://Sub-process Close (pipe_fd[0]); Turn off read-only 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_timeout )); 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 closed {CHK (Close (sock)); continue_to_work = 0; } else {printf ("%s\n", message); }} else//Receive information from child process {CHK 2 (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;}
"Go" Linux Expoll model