Libev's author wrote a very good official manual, the comparison is complete, that is introduced the Libev design idea, also introduced the basic use also includes the internal various kinds of events detailed introduction. Here's a little bit more. Libev through a struct ev_loop A knot structure represents an event-driven framework. Within this framework, the event monitor is registered through the event-driven framework of the structure, and the interface box, and when the event ev_xxx
ev_init
of the ev_xxx_set
ev_xxx_start
corresponding event monitor is present, the event Monitor's processing logic is triggered to handle the event. After processing, these monitors go into the next round of monitoring. A model that conforms to a standard event-driven state.
In addition to providing the basic three categories of events (IO events, timer events, signal events) also provides a period of events, sub-process events, file status change events and many other events, here we use three basic events to write an example, and manual on the similar, but did not do the finishing work, In order to clearly present the framework of the event.
#include <ev.h>#include<stdio.h>#include<signal.h>#include<sys/unistd.h>ev_io io_w;ev_timer timer_w;ev_signal signal_w;voidIo_action (structEv_loop *main_loop,ev_io *io_w,inte) { intrst; Charbuf[1024x768] = {' /'}; Puts ("In io cb\n"); Read (Stdin_fileno,buf,sizeof(BUF)); buf[1023] =' /'; printf ("Read in a string%s \ n", BUF); Ev_io_stop (Main_loop,io_w);}voidTimer_action (structEv_loop *main_loop,ev_timer *timer_w,inte) {Puts ("In Tiemr CB \ n"); Ev_timer_stop (Main_loop,timer_w);}voidSignal_action (structEv_loop *main_loop,ev_signal Signal_w,inte) {Puts ("In signal CB \ n"); Ev_signal_stop (Main_loop,signal_w); Ev_break (Main_loop,evbreak_all);}intMainintARGC,Char*argv[]) { structEv_loop *main_loop = Ev_default_loop (0); Ev_init (&io_w,io_action); Ev_io_set (&io_w,stdin_fileno,ev_read); Ev_init (&timer_w,timer_action); Ev_timer_set (&timer_w,2,0); Ev_init (&signal_w,signal_action); Ev_signal_set (&signal_w,sigint); Ev_io_start (Main_loop,&io_w); Ev_timer_start (Main_loop,&Timer_w); Ev_signal_start (Main_loop,&Signal_w); Ev_run (Main_loop,0);return 0;}
The following are descriptions of the APIs that are used.
3 event monitors are used to monitor IO events, timer events, and signal events, respectively. Therefore, 3 monitors (watcher) are defined, as well as a callback function to perform the action when the monitor is triggered. Libev defines a variety of monitors, named ev_xxx
here XXX represents the type of monitor, its implementation is a structure,
typedef struct ev_io{ ....} ev_io;
The macro definition can be abbreviated to ev_xxx
. The type of the callback function is void cb_name(struct ev_loop *main_loop,ev_xxx *io_w,int event)
.
In main, the structure of an event drive is defined first to struct ev_loop *main_loop
ev_default_loop(0)
generate a pre-fabricated global drive. Here you can refer to the selection in the manual. Then initialize each monitor in turn and set the trigger conditions for the monitor.
The process of initializing the monitor is to register the corresponding callback function, which is triggered, on the monitor.
Setting a trigger condition is when the condition is generated to perform the action registered on the monitor. For IO events, it is generally a matter of setting a readable or writable event on a specific FD, and how long the timer is triggered. Here the trigger condition of the timer also has a third parameter, indicating whether the first trigger, if the loop, if the 0 bar loop, otherwise by the value of the loop. The signal trigger is the signal that sets the trigger.
Once the trigger condition is initialized and set, the ev_xxx_start
monitor is first called to register on the event drive. The ev_run
start Event drive is then called.
Inside the trigger action of the event. I added a ev_xxx_stop
function that corresponds to the above, that is to say, the monitor is written off from the event drive. So that it no longer works. And in the action of the signal triggered also added a ev_break
function can cause the process to jump out of the main_loop
event drive cycle, that is, the Shutdown event drive. End this logic.
The simplest example of Libev is such a structure. Define a monitor, write trigger action logic, initialize the monitor, set the monitor trigger condition, and add the monitor to the loop of the large event drive. A more clear event-driven framework.
The Libev event-driven process can be imagined as the following pseudo-code:
= True while is_run: = caculate_loop_time () deal_loop (t) Deal_with_ Pending_event () do_some_clear ( )
Start by doing some initialization and then go into the loop, which controls whether execution is performed by a status bit. In the loop, the time of the next polling is calculated, and the implementation of the polling is based on the system-provided epoll, kqueue and other mechanisms. After polling, check which monitors have been triggered, and then execute the triggering action in turn. Don't dwell here. Signal events, Timer time I have been deal_loop
Libev how to achieve this is not discussed here for the moment, this pseudo-code is simply a general framework for the next Libev.
Libev Source code Structure
For graduates, especially those who have not contacted some of the existing engineering code. Get a copy of the source code, how to know it is the first problem to solve. I generally will be the source code classification: A class of products, such as Redis, Ngnix This class itself is a complete can be operations of mature products, and the other is Libev such a component class. For a component class project, I usually divide it into these steps:
- There are documents to see the document, no documents to ask the relevant people (including Google), this component is mainly provided by what services
- Combine the above information using the component's AIP to write an example program that runs up
- Roughly browse the source code and analyze the structure
- According to the API used, into the source code to see how the backbone is implemented, so as to understand the overall thinking
- Re-explore the source code, some of the auxiliary functions to see, and in the example to try
- The whole understanding is then recorded in words. Refining two chunks of content: tips for achieving ideas and tips
Here my study of Libev is in accordance with such a logic step-by-step.
EV.C Code Structure
In the article "Using Libev", a Libev official document was mentioned, and a simple example was written based on the document, including the 3 most commonly used event types, IO events, timer events, and signal events. In this article, you will analyze the code structure of Libev.
First download Libev source package, download back to unzip, Libev source is placed in the same directory, remove AutoConfig generated files, code files are more intuitive. The main. C and. h files are not too much to look at from the name. What do you guess? According to our example, the main extraction of which "ev.c ev_epoll.c ev_select.c ev.h ev_wrap.c ev_vars.c" in conjunction with our example to comb.
"Ev_epoll.c" and "ev_select.c" are support for the system-provided IO multiplexing mechanism "epoll", "select", and "port" for "poll", "Kqueue" Solaris, respectively "EV_POLL.C", " Ev_kqueue.c "," ev_port.c ". The concrete framework is similar, so as long as the analysis of one other is understood.
"Ev.h" is the definition of some APIs and variables, constants, "EV.C" is the main logic of Libev, where the definition of a type with a macro wrapper to declare member variables, in the file "EV_VARS.C". In order to simplify the statements used by member variables, a "EV_WRAP.C" is written. So we can look at these files, the main logic is in the "ev.c", where some constants, variables can be defined in the "ev.h", there is a structure of the member variable part of the definition in "EV_VARS.C", while the reference to the structure member variable through the "Ev_wrap.c" The file has a shorthand macro definition, and when a system is required to provide the underlying event interface, it is categorized in files such as "Ev_epoll.c", "ev_select.c", and so on.
Then open the "ev.c" file, "Ev.h" inside the various definitions, when needed to query can, through the IDE or VIM/EMACS combination of cscope/ctag can be a good solution. By browsing you can see that the code can be divided into three parts:
So you can jump directly to the code section. The separation point has a comment that the ECB ends. This can be done without worrying about the skipped parts, and so on when needed to go back to the review. Part of the ECB, as long as it knows its API function can, do not need to delve into, if the future need to come here to do a reference.
For a logical structure you can divide him into several parts:
This has a general understanding of the overall layout, you can have a selective breakthrough. Here you can also combine the official documents to understand each function. In order to provide a general understanding of the services provided by Libev.
Main data structures
A few important data structures are sorted out during the browsing process.
1. Time Type
typedef double ev_tstamp;
2. The ev_xx_ of the pit father
Libev ev_tstamp
is a double type variable that represents a time unit in essence.
structev_loop;# define Ev_pstructEv_loop *loop/*a loop as sole parameter in a declaration*/# define EV_P_ ev_p,/*a loop as first of multiple parameters*/# define EV_A loop/*a loop as sole argument to a function call*/# define Ev_a_ ev_a,/*a loop as first of multiple arguments*/# define EV_DEFAULT_UC ev_default_loop_uc_ ()/*The default loop, if initialised, as sole arg*/# define EV_DEFAULT_UC_ Ev_default_uc,/*The default loop as first of multiple arguments*/# define Ev_default ev_default_loop (0)/*The default loop as sole arg*/# define Ev_default_ ev_default,/*The default loop as first of multiple arguments*/
The definition here is still relatively human-free. "Ev_xxx" is equivalent to EV_XXX,
, so in the subsequent use of the API will be more concise, such as for the first parameter is struct ev_loop *loop
the callback function of the writing, you can write · void Io_action (ev_p ev_io *io_w,int e) ·. I don't know if the author has any other use here, I'm not very recommended here, but it's easier to understand when you look at the code later.
3. Various Watcher base classes
First look at a ev_watcher, this we can use Oo thought to understand him, he is equivalent to a base class, the subsequent ev_io what is derived from the agency, where the use of a compiler "unspoken rules" is the definition of variables and the order of the Declaration is consistent. This is also used in the LIBUV, and then the big God Yun-Feng brother also spat on it, you can see Yunfeng blog. I'll try to do it here. All the parts of the macro package are dialed out for easy understanding and viewing. Read the Libev code, I would like to marvel at the brilliance of its macro must also spit trough.
typedef struct ev_watcher{ int active; int pending; int priority; void *data; void (*cb)(struct ev_loop *loop, struct ev_watcher *w, int revents);} ev_watcher;
There is also a list of monitors installed with the base class.
typedef struct ev_watcher_list{ int active; int pending; int priority; void *data; void (*cb)(struct ev_loop *loop, struct ev_watcher_list *w, int revents); struct ev_watcher_list *next;} ev_watcher_list;
IO Monitor
typedef struct ev_io{ int active; int pending; int priority; void *data; void (*cb)(struct ev_loop *loop, struct ev_io *w, int revents); struct ev_watcher_list *next; int fd; /* 这里的fd,events就是派生类的私有成员,分别表示监听的文件fd和触发的事件(可读还是可写) */ int events; } ev_io;
Here, after stripping out of the macro, you can see that the private variables of the derived class are placed behind the common part. Thus, when a pointer to C is cast, a pointer p to the base class ev_watcher of the struct Ev_io object can be accessed through p->active to a member of the derived class that also represents active.
Timer Watcher
typedef struct ev_watcher_time{ int active; int pending; int priority; void *data; void (*cb)(struct ev_loop *loop, struct ev_watcher_time *w, int revents); ev_tstamp at; /* 这个at就是派生类中新的自有成员 ,表示的是at时间触发 */} ev_watcher_time;
The difference between the timer event watcher and Io here is that the timer is managed with a dedicated minimum heap. The monitors for other events, such as IO and signals, are linked by a single linked list, so he has no next member.
Signal Watcher
typedef struct ev_signal{ int active; int pending; int priority; void *data; void (*cb)(struct ev_loop *loop, struct ev_signal *w, int revents); struct ev_watcher_list *next; int signum; /* 这个signum就是派生类中新的自有成员 ,表示的是接收到的信号,和定时器中的at类似 */} ev_signal;
There are other events watcher's data structure is similar to this, you can look at the "Ev.h" code, here is no longer repeat. Finally, look at a type that can hold all the monitor objects:
union ev_any_watcher{struct ev_watcher W; struct ev_watcher_list WL; struct ev_io io; struct ev_timer timer; struct ev_periodic periodic; struct ev_signal signal; struct ev_child child; struct ev_stat stat; struct ev_idle idle; struct Ev_prepare prepare; struct ev_check check; struct ev_fork fork; struct ev_cleanup cleanup; struct ev_embed embed; struct ev_async async;};
4. The most important ev_loop
What struct ev_loop
is the structure of a forward statement that has been seen above? You can see this definition in "ev.c":
struct ev_loop{ ev_tstamp ev_rt_now; #define ev_rt_now ((loop)->ev_rt_now) #define VAR(name,decl) decl; #include "ev_vars.h" #undef VAR};#include "ev_wrap.h"
The previously mentioned "Ev_vars.h" and "Ev_wrap.h" are designed to define a data structure and to simplify access to its members, that is, the ev_loop structure. The macros used here are:
#define VAR(name,decl) decl;#define VARx(type,name) VAR(name, type name)
Unfold is
#define VARx(type,name) type name
Then look at "Ev_vars.h", which are all type-variable Varx macros, and then include them in the definition of the struct. This can be seen as the structure defined as:
struct ev_loop{ ev_tstamp ev_rt_now; ev_tstamp now_floor; int rfeedmax; ... .........;}
I don't know what the author's intentions are, and I haven't seen the benefits of doing so at the moment.
Then #define ev_rt_now ((loop)->ev_rt_now)
you can see it with the "ev_warp.h" in the back. In fact, that's #define xxx ((loop)->xxx)
it. When you want to use a struct ev_loop the member of an instance object loop, it can be written directly xxx
, and here again, EV_P EV_P_ EV_A EV_A_
we will find that in Libev's internal function, such a package can make the code a lot simpler. But it also increases the threshold for the first reading. Believe not to have seen Libev not to say its obscure.
5. Important Global Variables Default_loop_struct
In "ev.c" there are
static struct ev_loop default_loop_struct;
This is an instance object of the STRCT loop, which represents a pre-fabricated event drive. If you are using a pre-fabricated event drive in your code, the subsequent operations are all around this data structure.
For ease of operation, a global pointer to the object is also defined:
struct ev_loop *ev_default_loop_ptr
The framework of the Code and the main data structure of the comb out, there are ANFD, anheap and other data structures in the later analysis of the specific monitor is the time in detail. Follow the logic of the program to understand its design ideas, so that you can learn more about a component-based open source software.
Reprint: http://my.oschina.net/u/917596/blog/176915
Event-driven model Libev (i)