Analysis of Libev Source code

Source: Internet
Author: User
Tags epoll

Libev is an open source event-driven library based on the infrastructure provided by OS such as Epoll,kqueue. Known for its efficiency, it can unify IO events, timers, and signals to be processed in a single set of event-handling frameworks.

The basic use of Libev is as follows:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21st

int main (void)

{

Use the default event loop unless special needs

struct Ev_loop *loop = Ev_default;

Initialise an IO watcher and then start it

This one would watch for stdin to become readable

Ev_io_init (&stdin_watcher, STDIN_CB,/*stdin_fileno*/0, ev_read);//set to Stdin_watcher this FD focuses on read events and specifies the callback function

Ev_io_start (Loop, &stdin_watcher);//Activate Stdin_watcher this FD, set it to loop

Initialise a timer watcher, then start it

Simple Non-repeating 5.5 Second timeout

Ev_timer_init (&timeout_watcher, TIMEOUT_CB, 5.5, 0.); /Set a timer, and specify a callback function, this timer only executes once, 5.5s executes after

Ev_timer_start (Loop, &timeout_watcher);//Activate this timer and set it to loop

Now wait for events to arrive

Ev_run (loop, 0);//Cycle start

Break is called, so exit

return 0;

}

There is an abstract concept in Libev, called Watcher (Ev_watcher), Libev has a variety of watcher, such as timer watcher (struct ev_timer), I/O watcher (struct ev_io), Signal Watcher (struct ev_signal)

Wait a minute. These three specific watcher correspond to subclasses of the parent class watcher. This kind of inheritance is built into the high-level language of C + + and requires some tricks to implement in C, and Libev's authors use macros.

The "Parent class" Ev_watcher is defined as follows:

typedef structev_watcher

{

"Color: #ff0000;" >ev_watcher (Ev_watcher)

} Ev_watcher;

The macro ev_watcher is defined as follows:

/* Shared by all watchers */

#define Ev_watcher (type) \

int active;/* Private */\//The Watcher is activated, added to loop

int pending;/* Private */\//The Watcher concerns whether events have been triggered

ev_decl_priority/* private */\//int priority; Priority, Watcher is a priority

ev_common/* rw */\//void *data;

Ev_cb_declare (type)/* private *///Void (*CB) (struct Ev_loop *loop, type *w, int revents); callback function

Then look at the definition of a "parent" ev_watcher_list:

Typedefstructev_watcher_list

{

Ev_watcher_list (Ev_watcher_list)

} ev_watcher_list;

The macro ev_watcher_list is defined as follows:

#define EV_WATCHER_LIST (type) \

"Color: #ff0000;" > ev_watcher (type) \

Structev_watcher_list *next; /* Private */

As can be seen, ev_watcher_list is actually a "subclass" of Ev_watcher, it has a member variable struct ev_watcher_list *next;

This member variable is used to string the watcher together.

Now look at an I/O watcher the most important "subclass":

typedef struct EV_IO

{

Ev_watcher_list (Ev_io)

int FD; /* Ro *///Obviously, the FD associated with IO

int events;/* ro *///This watcher event of interest on FD

} ev_io;

As can be seen, Ev_io is a concrete watcher, it has two own proprietary member variables FD and Events

Here's a look at one of the most critical data structures:

struct EV_LOOP

{

Ev_tstamp Ev_rt_now;

#define Ev_rt_now ((loop)->ev_rt_now)

Here decl is declare meaning, ev_vars.h inside will define a bunch of variables, these variables

Are members of this structure, EV_VARS.H will use the following line of VAR macros when expanding

#define VAR (NAME,DECL) decl;

#include "Ev_vars.h"

#undef VAR

};

The ev_vars.h contains a number of key members, such as:

Epoll-related member variables:

#if Ev_use_epoll | | Ev_genwrap

Varx (struct epoll_event *, epoll_events)//equivalent to struct epoll_event *epoll_events

Varx (int, Epoll_eventmax)//Current epoll_events array size can be expanded to twice times the size of each expansion

Varx (int, BACKEND_FD)//For Epoll, it is the FD used by Epoll

For Epoll, the actual function is the epoll_modify function in ev_epoll.c, and this function executes EPOLL_CTL

VAR (backend_modify, Void (*backend_modify) (Ev_p_intfd,intoev,intnev))

For Epoll, the actual function is the Epoll_poll function in ev_poll.c, and this function executes epoll_wait

VAR (Backend_poll, Void (*backend_poll) (Ev_p_ ev_tstamp timeout))

FD-related member variables:

Varx (ANFD *, Anfds)//This array is indexed with FD

Varx (int, Anfdmax)//size of the above array

Varx (int*, fdchanges)//Fdchangemax An array of size, each element is an FD, and this array has all the epoll needed to poll the FD

Varx (int, Fdchangemax)//array capacity

Varx (int, fdchangecnt)//the size of the actual element in the array

ANFD and FD one by one correspond to the structure ANFD as follows:

Typedefstruct

{

WL Head; typedef ev_watcher_list *WL; Focusing on the Watcher linked list of events of the same FD, an FD can have multiple watcher to monitor its events

unsigned char events;/* the event watched for *///watcher list of all watcher-focused events for the bitwise AND

unsigned char reify; /* Flag set when this ANFD needs reification (ev_anfd_reify, Ev__iofdset) *///If the structure needs to be re-epoll_ctl then set, indicating that the event of concern has changed

Unsignedcharemask; /* The Epoll backend stores the actual kernel mask in here *///events actually occurred

unsignedcharunused;

#if Ev_use_epoll

unsigned int egen; /* Generation counter to counter epoll bugs * *

#endif

#if Ev_select_is_winsocket | | Ev_use_iocp

SOCKET handle;

#endif

#if EV_USE_IOCP

OVERLAPPED or, ow;

#endif

} ANFD;

To maintain all the "Watcher of the event of concern", these watcher callback eventually need to be called

VAR (pendings, anpending *pendings [Numpri])//watcher has a priority, Libev maintains an array for each priority watcher

VAR (Pendingmax, Intpendingmax [NUMPRI])//capacity of each priority watcher array

VAR (pendingcnt, intpendingcnt [NUMPRI])//Per priority Watcher array actual size

The struct anpending is as follows:

Typedefstruct

{

W W; typedef ev_watcher *W;

int events;/* the pending event set for the given watcher *///events refers only to events that have not been handled by this watcher concern and have already occurred

} anpending;

As you can see from the sample program, there are several main ways to use Libev:

Ev_io_init

Ev_io_start

Ev_timer_init

Ev_timer_start

Ev_run

Look at:

Ev_io_init is a macro, which is primarily about setting up individual members in a ev_io

The void Ev_io_start (struct ev_loop *loop, ev_io *w) will do several things as follows:

1. Watcher activation (w->active=1) of the parameter W representation

2. Add Watcher W to the Watcher list list in the structure ANFD in the corresponding position of the FD in anfds[] (loop) where W is concerned.

3. Add the FD that is interested in W to the int *fdchanges array.

The most important function of void Ev_run (struct ev_loop *loop, int flags) is to do several things as follows:

1. Call Fd_reify.

Traversing the fdchanges array: For each of these FD, do the following:

All the watcher that focus on this FD are taken out of the ANFD list (by FD go to anfds[] to find the corresponding ANFD structure), and then all of the watcher-focused events are set by Epoll_ctl to

The kernel. Then empty the fdchanges array, in fact, the fdchangecnt is set to 0.

2. Time_update update time, calibration time

3. Calculate the timeout used for epoll_wait (), take the heap top (timers [HEAP0]) from the smallest heap that maintains all the timer, and subtract the current time to get a timeout. The minimum heap is implemented using an array, and each element is

struct Anhe.

Timers is defined as follows:

Varx (Anhe *, timers)

The struct anhe is defined as follows:

/* A HEAP element */

typedef struct{

Ev_tstamp At;//timer Watcher Expiry time

WT w;//typedef ev_watcher_time *WT;

} Anhe;

typedef struct EV_WATCHER_TIME

{

Ev_watcher_time (Ev_watcher_time)

} ev_watcher_time;

#define EV_WATCHER_TIME (type) \

Ev_watcher (type) \

Ev_tstamp at; /* Private */

4. Calling Backend_poll (Loop, waittime) will do several things:

For a system that uses epoll, it is actually called the Epoll_poll () function in ev_epoll.c, the main flow of the function is as follows:

4.1. Call Epoll_wait ()

4.2. Iterate through each returned struct epoll_event, take out the FD, and activate the event in Epoll_event, call the Fd_event (loop, FD, got) function.

This function updates the loop's pending array, watcher those that have focused on some of the events, and those watcher that do occur to the pending array set into the loop.

Note here that got is the sum of all the events that watcher are concerned about. Events in the anpending structure are only a single watcher concern.

5. Time_update Update the calibration time again

6. Timers_reify (loop) If the most recent timer has been triggered, re-select the next closest timer and place it on top of the heap.

7.periodics_reify (loop) and timers treatment are almost

8. Idle_reify (loop) not paying attention

9. Ev_invoke_pending calls the callback functions of each watcher in order from the PENDING array in loop, with priority from high to low

10. If there are also active watcher in the current loop, and Loop_done ==0 and the parameters of the boot Ev_run (flags) are not set evrun_once and evrun_nowait, continue starting with 1.

So far, the timer and I/O events have been unified, and now see how the signal is unified into the event processing framework.

Look at the definition of signal watcher:

/* Invoked when the given signal have been received */

/* revent ev_signal */

Typedefstructev_signal

{

Ev_watcher_list (ev_signal)

intsignum;/* RO *///Signal ID

} ev_signal;

Ansig signals [ev_nsig-1]; The information for each signal is represented by a ANSIG structure

Typedefstruct

{

ev_atomic_t pending;

#if ev_multiplicity

ev_p;

#endif

WL head;//can have more than one watcher focus on the same signal, using a linked list to string up

}ansig;

The main members associated with loop and signal are as follows:

VAR (Evpipe, Intevpipe [2])//For combining signal processing and event processing frameworks, Evpipe[0] for reading, evpipe[1] for writing

Varx (Ev_io, Pipe_w)//This I/O ev is used to encapsulate the read end of the pipe above, let the epoll listen to this pipe_w, when the signal is received, only need to in the signal processing function to evpipe[1] Write

Similar to the I/O event, the timer event, has the following two procedures:

EV_SIGNAL_INIT: Macro, initialize this ev_signal struct

void Ev_signal_start (struct ev_loop *loop, ev_signal *w) do several things:

1. Activate ev_signal this watcher (w->active = 1)

2. String W to the Watcher list in the corresponding position in the signals array

3. Initialize a pair of pipe, which is used to combine the signal and event processing framework, and register a ev_io pipe_w to Epoll, pipe_w encapsulated int evpipe[2] read-end evpipe[0]

4. Register this signal processing function ev_sighandler, the signal processing function will signals the corresponding slot in the array pending 1, meaning that it has received this signal, and to the write end of the int evpipe[2]

EVPIPE[1] Writes a byte so that in Pipe_w this IO event has a read event that epoll_wait () returns, successfully combining the signal and event processing framework.

At this point, Libev how to unify IO events, timer events and signals into the event processing framework has been analyzed. It can be seen that the Libev and libevent practices are the same, but the implementation is not the same, for example, on the implementation of the inheritance, Libev use of macros to achieve quite difficult to read, but this is efficient, borrow the words of the boss: "In the Libev World, efficiency first". This is probably the reason most people say that Libev is harder to read than Libevent code


Analysis of Libev Source code

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.