epoll Reactor model demo Realization

In high concurrency TCP In request , In order to save resources , Efficiency improvement ,Epoll Gradually replaced the previous select and poll, It avoids the inefficient monitoring method of busy polling at the user level ,epoll The time complexity of is O(1), That means ,epoll In high concurrency scenarios , As file descriptors grow , Good scalability .

  • select and poll Listen for file descriptors list, A linear search O(n)
  • epoll: The callback mechanism at the kernel file level is used O(1)

There are three key functions :

  • epoll_create(int size): Create a epoll example , File descriptor

    Return an object , That is, the roots of red and black trees , In code implementation, most of them are global variable file descriptors .size Parameters are estimates of connections , Only reference value for kernel , It doesn't matter if you actually exceed

  • epoll_ctl(int epfd, int op, int fd, struct epoll_event *event):

    This system call is used to add 、 Modify or delete the list descriptor of the file reference epfd. It requires operations op For the target file descriptor fd.op The operation of has add , Delete , change . The event parameter describes the object descriptor linked to the file fd. The data type is the core structure below epoll_event.

  • epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout):

    wait for epoll Events from epoll In this case , And return the event and the corresponding file descriptor .

    When timeout Greater than 0 Time is the specified time limit , be equal to 0 Non blocking , Less than 0 For blocking ,maxevents by events The maximum number of elements that can be received , That is, the maximum number of return events ,events Is an outgoing array , It is used to receive return events .

Core data structure

Click to view the code
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 */
};

principle

When a process calls epoll_create When the method is used ,Linux The kernel will create a eventpoll Structure , There are two members of this structure with epoll The way in which they are used is closely related , As shown below :

struct eventpoll {

  ...

  / The root node of the red black tree , This tree stores everything added to epoll In the event ,

   That's it epoll Monitored events
/

  struct rb_root rbr;

  / Double linked list rdllist Keep it going through epoll_wait Returned to the user 、 Events that meet the conditions /

  struct list_head rdllist;

  ...

};

We're calling epoll_create when , The kernel can help us in the epoll There's one in the file system file node , In the kernel cache Built a red and black tree in it for storage epoll_ctl From the socket Outside , There will be another rdllist Double linked list , Used to store events that are ready , When epoll_wait Invocation time , Just look at this rdllist Whether there is data in the two-way linked list . If there is data, return it , Without data sleep, wait until timeout When the time is up, even if the linked list has no data, it will return . therefore ,epoll_wait Very efficient .

All added to epoll The events in will be related to the device ( Such as network card ) The driver establishes a callback relationship , In other words, the callback method here will be called when the corresponding event occurs . This callback method in the kernel is called ep_poll_callback, It will put such events on it rdllist In a two-way list .

stay epoll For each event, a epitem Structure , As shown below :

struct epitem {

  ...

  // Red black tree node

  struct rb_node rbn;

  // Two-way list node

  struct list_head rdllink;

  // Event handle and other information

  struct epoll_filefd ffd;

  // Point to what it belongs to eventepoll object

  struct eventpoll *ep;

  // Expected event type

  struct epoll_event event;

  ...

}; // This contains the information corresponding to each event .

When calling epoll_wait When checking whether there is a connection with an event , Just check eventpoll Object rdllist Whether the two-way linked list has epitem It's just elements , If rdllist Link list is not empty , Then the events here are copied to the user state memory ( Use shared memory to improve efficiency ) in , At the same time, the number of events is returned to the user . therefore epoll_waitx Very efficient .epoll_ctl In the epoll Add to object 、 modify 、 When deleting an event , from rbr Finding events in the red black tree is also very fast , in other words epoll It's very efficient .

Trigger mode

epoll Yes EPOLLLT and EPOLLET Two trigger modes ,LT It's the default mode ,ET yes “ High speed ” Pattern .

LT( Level trigger ) In mode , As long as the file descriptor has data to read , Every time epoll_wait Will return its events , Remind the user to operate the program ;

ET( Edge trigger ) In mode , Before it detects I/O When an event is , adopt epoll_wait Call to get a file descriptor with event notification , For each notified file descriptor , As readable , The file descriptor must be read all the way to empty , Give Way errno return EAGAIN until , Or next time epoll_wait The rest of the data will not be returned , Will lose the event . If ET The mode is not non blocking , Then the one that has been reading or writing is bound to block at the last time .

Another characteristic is ,epoll Use “ event ” Is ready to inform , adopt epoll_ctl register fd, Once it's time to fd be ready , The kernel uses something like callback Call back mechanism to activate the fd,epoll_wait You can receive the notice .

【 summary 】:

ET Pattern ( Edge trigger ) Only the arrival of data triggers , Whether there is data in the cache or not , The remaining unread data in the buffer will not cause epoll_wait return ;

LT Pattern ( Level trigger , Default ) As long as there is data, it will trigger , The remaining unread data in the buffer will cause epoll_wait return .

Reactor model flow

【epoll Process of reactor model 】:

epoll_create(); // Create a monitoring red black tree

epoll_ctl(); // Add listening to the book fd

epoll_wait(); // monitor

There is a client connection --->lfd call acceptconn()---> take cfd Mount to the red black tree to listen for its reading events --->

epoll_wait() return cfd--->cfd Callback recvdata()---> take cfd Take it off to listen and write events --->

epoll_wait() return cfd--->cfd Callback senddata()---> take cfd Take it off to listen and read Events --->...--->

demo

Click to view the code
/*
epoll Based on non blocking I/O Event driven
*/
#include <stdio.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <time.h> #define MAX_EVENTS 1024
#define BUFLEN 4096
#define SERV_PORT 8080 void recvdata(int fd ,int events , void*arg);
void senddata(int fd ,int events , void*arg); struct myevent_s
{
int fd; // The file descriptor to listen to
int events; // The corresponding listening event
void *arg; // Generic functions
void (*call_back)(int fd, int events,void *arg); // Callback function
int status; // Is it listening :1 On the red and black trees ( monitor ) 0 be not in
char buf[BUFLEN]; // buffer
int len; // Size of buffer
long last_active; // Record the time of joining the red black tree
}; int g_efd; // Global variables Record epoll_create File descriptor returned
struct myevent_s g_events[MAX_EVENTS+1]; // Custom structure type array // Initialize structure myevent_s Member variables
void eventset(struct myevent_s *ev , int fd, void (*call_back)(int, int ,void*),void *arg)
{
ev->fd=fd;
ev->events=0;
ev->call_back=call_back;
ev->status=0;
ev->arg = arg;
if(ev->len <= 0)
{
memset(ev->buf, 0, sizeof(ev->buf));
ev->len = 0;
}
ev->last_active= (time)NULL;
return;
} /* Add a file descriptor to the listening red black tree */
void event_add(int efd , int events , struct myevent_s *ev){
struct epoll_event epv = {0,{0}};
int op;
epv.events = ev->events = events;
epv.data.ptr = ev; if(ev->status == 0 ){
op = EPOLL_CTL_ADD; // Add it to the red black tree gfd in
ev->status = 1;
}
if(epoll_ctl(efd,op,ev->fd,&epv)<0){
printf("epv add failed\n");
}
else
printf("epv add succeed\n");
} void eventdel(int efd ,struct myevent_s *ev){
struct epoll_event epv = {0,{0}};
if(ev->status != 1){
return;
}
epv.data.ptr = NULL;
ev->status = 0;
epoll_ctl(efd,EPOLL_CTL_DEL,ev->fd, &epv);
return ;
} void acceptconn(int lfd , int events ,void *arg){
struct sockaddr_in cin ;
socklen_t len = sizeof(cin);
int cfd , i;
if((cfd=accept(lfd,(struct sockaddr*)&cin,&len))== -1)
{
printf("accept error\n");
return;
}
else
{
printf("connect success\n");
}
do
{
for (i =0;i<MAX_EVENTS;i++)
if(g_events[i].status == 0)
break;
if(i == MAX_EVENTS){
printf("connect limit\n");
break;
}
int flag = 0;
if((flag= fcntl(cfd,F_SETFL,O_NONBLOCK))< 0 ){
printf("nonblock failed\n");
break;
} eventset(&g_events[i],cfd,recvdata,&g_events[i]);
event_add(g_efd,EPOLLIN,&g_events[i]);
} while (0);
return;
}
// receive data
void recvdata(int fd,int events, void *arg){
struct myevent_s *ev = (struct myevent_s *)arg;
int len;
len = recv(fd , ev->buf,sizeof(ev->buf),0); // Read file descriptor , Data stored in myevent_s in
eventdel(g_efd, ev); // Remove the node from the red black tree
if(len>0){
ev->len = len;
ev->buf[len] = '\0'; // Add the end flag manually
eventset(ev,fd,senddata,ev);
event_add(g_efd,EPOLLOUT,ev); // take fd Add to g_efd Dictation incident of the central prison }
else if(len == 0){
close(ev->fd);
printf("recv null and close");
}
else{
close(ev->fd);
printf("recv error");
}
} void senddata(int fd ,int events , void *arg){
struct myevent_s *ev = (struct myevent_s *)arg;
int len;
len = send(fd,ev->buf,ev->len,0); // Write directly back to the client .
eventdel(g_efd,ev); // Remove... From the red and black tree
if(len>0){
printf("send success");
eventset(ev,fd,recvdata,ev);
event_add(g_efd,EPOLLIN,ev);
}
else{
close(fd);
printf("send error");
}
} void initlistensocket(int efd , short port){
struct sockaddr_in sin; int lfd = socket(AF_INET,SOCK_STREAM,0);
fcntl(lfd,F_SETFL, O_NONBLOCK); // hold socket Set to non blocking memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_addr.s_addr =INADDR_ANY;
sin.sin_port = htons(SERV_PORT); bind(lfd, (struct sockaddr *)&sin,sizeof(sin));
listen(lfd , 128);
eventset(&g_events[MAX_EVENTS],lfd,acceptconn,&g_events[MAX_EVENTS]); event_add(g_efd,EPOLLIN,&g_events[MAX_EVENTS]); } int main(){
int port = SERV_PORT;
g_efd = epoll_create(MAX_EVENTS+1); // Create a red black tree and return it to the global file descriptor
if(g_efd<=0){
printf("create epoll error");
}
initlistensocket(g_efd,port); // Initialize listening socket struct epoll_event events[MAX_EVENTS+1]; // Save an array of file descriptors that have satisfied the ready event by epoll_wait To prepare for
printf("server running:port[%d]\n", port); int checkpos = 0 ,i;
while (1)
{ // Timeout verification
/*long now = time(NULL);
for(i = 0 ;i<100;i++){
if(checkpos == MAX_EVENTS)
checkpos = 0;
if(g_events[checkpos].status != 1 ){
continue; // Not on the black and red tree
} long duration = now - g_events[checkpos].last_active;
if(duration >= 60){
close(g_events[checkpos].fd);
printf("timeout");
eventdel(g_efd,&g_events[checkpos]);
}
}*/ // Monitor the red black tree g_efd, Add the satisfied event to g_events in .
int nfd = epoll_wait(g_efd,events,MAX_EVENTS+1,1000);
if(nfd<0){
printf("epoll_wait error");
exit(-1);
} for(i = 0 ;i < nfd ;i++){
struct myevent_s *ev = (struct myevent_s *) events[i].data.ptr;
if((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)){ // Read ready event
ev->call_back(ev->fd,events[i].events,ev->arg);
}
if((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)){ // Write ready event
ev->call_back(ev->fd,events[i].events,ev->arg);
}
} } }

Usage method :
gcc server.c -o server
./server
Reopen the new terminal :
nc 127.1 8080

epoll More articles on reactor model implementation

  1. epoll Reactor model

    ================================ The idea of the following code implementation :epoll Reactor model :( libevent Open source library for network programming The core idea ) 1. General multiplex IO Transfer server : Red and black trees ― ...

  2. epoll A detailed explanation of the principles and epoll Reactor model

    Reprinted from epoll A detailed explanation of the principles and epoll Reactor model Introduction Imagine a scenario : Yes 100 Ten thousand users are keeping up with a process at the same time TCP Connect , There are only a few dozen or a few hundred at a time TCP The connection is active ( receive TCP package ), That is to say, at every moment ...

  3. epoll Reactor model code

    libevent The core idea of function library /*** epoll_loop.c ***/ #include<stdio.h> #include<sys/epoll.h> #include& ...

  4. epoll Reactor

    epoll Reactor model ================================ The idea of the following code implementation :epoll Reactor model :( libevent Open source library for network programming The core idea ) . General multiplex IO transfer ...

  5. ( turn )Linux Next select, poll and epoll IO Detailed explanation of the model

    Linux Next select, poll and epoll IO Detailed explanation of the model original text :http://blog.csdn.net/tianmohust/article/details/6677985 One ).Epoll ...

  6. Linux Next select, poll and epoll IO Detailed explanation of the model

    http://blog.csdn.net/tianmohust/article/details/6677985 One ).Epoll Introduce Epoll But at the moment Linux Development of large-scale concurrent network program under the hot ...

  7. epoll Event model

    Event model EPOLL There are two models of events : Edge Triggered (ET) Edge trigger is triggered only when data arrives , Whether there is data in the cache or not . Level Triggered (LT) Horizontal trigger will touch as long as there is data ...

  8. Nginx Epoll The advantages and disadvantages of the event model

    L30-31 Epoll The main performance advantage is that it doesn't need to traverse Suppose there is 100 Ten thousand Links Other events may need to traverse all links , and Epoll Just traverse the active links , This greatly improves efficiency

  9. Linux Study notes --- epoll Event model details

    epoll It is mainly used for the ready fd Polling operation   One .epoll Trigger mode epoll Support ET and LT Two trigger modes ET( Edge trigger ):Nginx Is to adopt ET Trigger mode , Only support no-b ...

  10. epoll Reactor

    /* * epoll Based on non blocking I/O Event driven */ #include <stdio.h> #include <sys/socket.h> #include <sys/ep ...

Random recommendation

  1. to update / Replacement system hosts, Easy access to foreign sites

    to update hosts The operations described below may cover the existing hosts , Please confirm whether backup is needed before operation . It is recommended to use  Host Tools  From dynamic Backup / To configure Work . If update hosts Not effective immediately , please ...

  2. IntelliJ IDEA 12 And Tomcat7 To configure

    IDEA Full name IntelliJ IDEA, yes java Integrated environment for language development ,IntelliJ Recognized as the best in the industry java One of the development tools , Especially in smart code assistants . Code autoprompt . restructure .J2EE Support . Various versions of tools ( ...

  3. 10,SFDC Administrator - Process automation

    1,Process Builder Setup | Build | Create | Workflow & Approvals | Process Builder When we create or modify a in an object ...

  4. .net File operations

    One .DotNet Common operations of file directory : DiveInfo: It provides access to the basic information of the logical disk .( You can only view information , Can't make any changes .) System.Environment: Used to enumerate drives .( Unable to get drive ...

  5. SimpleDateFormat Performance and thread safety of

    After a period of normal operation of the system ,QA Give me an exception : java.lang.OutOfMemoryError: GC overhead limit exceeded at java.text.DecimalFo ...

  6. stay windows Lower installation mysql

    This article mainly talks about mysql Decompressed version in windows Under the installation and configuration , On the official website http://www.mysql.com/ download mysql-cluster-gpl-7.3.7-winx64.zip, And then mysql decompression ...

  7. TagHelper+Layui Package components Radio Radio buttons

    TagHelper+Layui Package components Radio Radio buttons Tag name :cl-radio Tag attributes : asp-for: Bound fields , Must specify asp-items: Bind single option The type is :IEnumerable& ...

  8. [Go] Use go Language solves modern programming problems

    1. Computers have been evolving ,64 nucleus ,128 Nuclear, etc , But we still use the technology designed for single core 2.Go Language makes it easier to share your own code package 3.Go Language rethinks traditional object-oriented , It provides a more efficient means of reusing code 4.Go Not only provide high performance ...

  9. PowerShell- Custom function ( 5、 ... and )- Parameters are mutually exclusive :ParameterSetName

    from :https://blog.51cto.com/38088444/1920978 In this article, we will talk about the mutual exclusion of parameters , What is mutual exclusion of parameters . In the words of jiupang style, it is mutual connection , Yes, I don't have you , With you and without me . For example, we are a Pin ...

  10. php forge ip head

    <? $fp = fsockopen ("passport.baidu.com", 80, $errno, $errstr, 30); if (!$fp) { echo &q ...