From the main function, convenient to master the operation mechanism of Redis from the macro, this article from the main function, from the top, the main call what interface, specifically completed what function, and then focus on the specific module.
Aeeventloop is the event core data structure for Redis, and Redis will adapt aeeventloop to multiple separators on different platforms, such as Select/kqueue/epoll.
In order to cross-platform, Aeeventloop defines the structure of void* apidata, which is used to correlate separators with different platforms.
1typedefstructAeeventloop {2 //Maximum currently registered descriptor3 intMAXFD;/*highest file descriptor currently registered*/4 //Maximum descriptor currently tracked5 intSetSize/*max number of file descriptors tracked*/6 //used to generate the time event ID7 Long LongTimeeventnextid;8 //time of the last Execution time event9time_t Lasttime;/*used to detect system clock skew*/Ten //Registered file Events OneAefileevent *events;/*Registered Events*/ A //ready-to-file events -Aefiredevent *fired;/*fired Events*/ - //Time Events theAetimeevent *Timeeventhead; - //switches for event handlers - intstop; - //private data for multiplexed libraries + void*apidata;/*This was used for polling API specific data*/ - //functions to be executed before the event is processed +Aebeforesleepproc *Beforesleep; A} Aeeventloop;
Where Select's Apidata structure is:
struct aeapistate { fd_set RfDs, Wfds; /* We need to has a copy of the FD sets as it 's not safe to reuse */ fd_set _rfds, _wfds;} aeapistate;
The additional introduction of _rfds,_wfds, which is mentioned here, is a copy of the read-write handle set, since the reuse of the FD collection is not secure after the select call is complete (for specific reasons, select function parsing is possible).
When creating a eventloop, the mask of each event (i.e. EVENTLOOP->EVENTS[FD]) in the eventloop->events array is preset ae_none;
/* Events with mask = = Ae_none is not set. So let ' s initializethe* /for0; i < setsize; i++) EventLoop- >events[i].mask = Ae_none;
When the first event comes,
Static intAeapipoll (Aeeventloop *eventloop,structTimeval *TVP) {Aeapistate*state = eventloop->Apidata; intRetVal, J, numevents =0; memcpy (&state->_rfds,&state->rfds,sizeof(Fd_set)); memcpy (&state->_wfds,&state->wfds,sizeof(Fd_set)); retval=Select(eventloop->maxfd+1, &state->_rfds,&state->_WFDS,NULL,TVP); if(RetVal >0) { for(j =0; J <= eventloop->maxfd; J + +) { intMask =0; Aefileevent*fe = &eventLoop->Events[j]; if(Fe->mask = = Ae_none)Continue; if(Fe->mask & ae_readable && fd_isset (j,&state->_rfds)) Mask|=ae_readable; if(Fe->mask & ae_writable && fd_isset (j,&state->_wfds)) Mask|=ae_writable; EventLoop->FIRED[NUMEVENTS].FD =J; EventLoop->fired[numevents].mask =Mask; Numevents++; } } returnnumevents;}
The Fe->mask is judged here, and if it is Ae_none, this FD has no readable or writable events and skips this for loop.
Otherwise, they are judged to be readable or writable, and the mask of Fe is constructed. Finally, the FD associated with this event (that is, J here) is added to the fired array, which is all of the Ready FD arrays.
Obviously,the role of Aeapipoll is to use a multi-channel separator, multiplexing IO, to determine which FD can read and write, and add to the ready array, waiting to be processed .
So when will aeapipoll be called?
Called in the function aeprocessevents, gets the ready FD, completes the read/write Io.
The key code at the call is as follows:
Numevents =Aeapipoll (EventLoop, TVP); for(j =0; J < Numevents; J + +) { //getting events from a ready arrayAefileevent *fe = &eventLoop->events[eventLoop->FIRED[J].FD]; intMask = eventloop->Fired[j].mask; intFD = eventloop->FIRED[J].FD; intrfired =0; /*Note the Fe->mask & mask & Code:maybe A already processed * event removed an element tha T fired and we still didn ' t * processed, so we check if the event is still valid. */ if(Fe->mask & Mask &ae_readable) { //Read Eventsrfired =1;//ensure that read/write events can only perform one of theFe->rfileproc (eventloop,fd,fe->clientdata,mask); } if(Fe->mask & Mask &ae_writable) { //Write Events if(!rfired | | Fe->wfileproc! = fe->rfileproc) Fe->wfileproc (eventloop,fd,fe->clientdata,mask); } Processed++;
Simple analysis of the specific details:
Compute TVP (that is, timeout for the multiplexer, timeout, and if NULL for the last parameter of select, then select blocks until a handle can be read and write)
Call Numevents=aeapipoll (EventLoop, TVP), return the number of events that can read and write, iterate over the eventloop->events array, which takes the FD as the Subscript index event, and FD is stored in the eventloop-> The fired array.
So the index logic from numevents to FE is as follows:
The event is readable, then the call to the fe->rfileproc; event is writable, then the Fe->wfileproc is called to write Io. And the initialization of Fe->rfileproc,fe->wfileproc is in Initserver;
The main code for Initserver is as follows:
//register the new number handler function, for Sighup,sigpipe, ignore. //initializing a Shared object//key, initialize the Redis event loop main structureServer.el = Aecreateeventloop (server.maxclients+1024x768);//initializing a network connection as a serverSERVER.IPFD =Anettcpserver (server.neterr,server.port,server.bindaddr);//Initialize DB//Initialize PubSub//key, associated time timeAecreatetimeevent (Server.el,1, Servercron, NULL, NULL);//Associate the previously created server socket, SERVER.IPFD, with the Redis event loop. if(Server.ipfd >0&&aecreatefileevent (server.el,server.ipfd,ae_readable, Accepttcphandler,null)= = Ae_err) redispanic ("unrecoverable Error creating SERVER.IPFD file event.");//Initialize Bio
The key function is aecreatefileevent, which will be server.ipfd in Server.el, and Accepttcphandler will be used to initialize Fe->rfileproc, and fe-> Wfileproc.
Entering the Aecreatefileevent function, this function is more important, and we analyze it by line:
intAecreatefileevent (Aeeventloop *eventloop,intFdintMask, Aefileproc*proc,void*clientdata) { if(FD >= eventloop->setsize)returnAe_err; Aefileevent*fe = &eventLoop->EVENTS[FD]; //listen for specified FD if(Aeapiaddevent (EventLoop, FD, mask) = =-1) returnAe_err; //Set File Event typeFe->mask |=Mask; if(Mask & ae_readable) Fe->rfileproc =proc; if(Mask & ae_writable) Fe->wfileproc =proc; Fe->clientdata =Clientdata; //if necessary, update the maximum FD for the event handler if(FD > Eventloop->maxfd) EventLoop->MAXFD =FD; returnAe_ok;}
Above this call Aecreatefileevent (server.el,server.ipfd,ae_readable, Accepttcphandler,null)
Where EventLoop is the Redis core event loop, FD is server.ipfd,mask for Ae_readable,proc=accepttcphandler, i.e. Fe->rfileproc and fe-> Wfileproc are Accepttcphandler,
Clientdata is null.
Summary: After the server side establishes the service connection, the socket that is used to receive the client response is SERVER.IPFD (socket (af_inet,sock_stream,0) to create the sockets), the created socket listener read events, The corresponding event handler function is Accepttcphandler.
To view the Accepttcphandler function code:
// where accept is called, create CFD service client cfd = anettcpaccept (Server.neterr, FD, CIP, &Cport)// Create redisclient for CFD, get client query command // call Readqueryfromclient () to get client buffer. Acceptcommandhandler (CfD,0)
The CFD creates a socket for the response from the client, which serves this client. Create a redisclient for the CfD in Acceptcommandhandler. The key code is:
Static voidAcceptcommonhandler (intFdintflags) {redisclient*C; //Create a new client if((c = createclient (FD)) = =NULL) {Redislog (redis_warning,"Error Allocating resources for the client"); Close (FD); /*May is already closed, just ignore errors*/ return; } /*If maxclient directive is set and this is a client more ... close the * connection. Note that we create the client instead-check before * for this condition, since now the socket was already set in no Nblocking * Mode and we can send an error to free using the Kernel I/O*/ if(Listlength (server.clients) >server.maxclients) {Char*err ="-err max number of clients reached\r\n"; /*that's a best effort error message, don ' t check write errors*/ //send error message to client if(Write (C->fd,err,strlen (err)) = =-1) { /*Just to avoid the warning ...*/} server.stat_rejected_conn++; //releasing the clientfreeclient (c); return; } server.stat_numconnections++; C->flags |=flags;}
Where call CreateClient creates a redisclient for the handle of the CFD, the socket is o_nonblock in createclient and the Tcp_nodelay attribute, that is, non-blocking and no-delay modes.
At the same time, this function adds read events to the CFD to listen for command requests from the client. Specific details, in the later series in-depth analysis.
Summary of Series 1: from the main function entrance of REDIS.C, do the necessary initialization, configure the read, create the core event loop of Redis EventLoop, create the server network connection, and associate the event loop with the server's network connection. and the multi-path separation mechanism of Redis is realized based on the IO multiplexing mechanism of different platforms.
Based on this, you can draw a mind map that is easy to understand:
The small flag is marked out as a key understanding part.
Redis Source Analysis Series 1-main function related call analysis