Libev source code analysis-Overall Design

Source: Internet
Author: User
Document directory
  • Ev_loop Definition
  • Watcher Management

Libev is a high-performance event loop library written by Marc Lehmann in C. With libev, You can flexibly organize and manage various events, such as clock, io, and signal. Libev is also widely acclaimed in the industry, and many projects use it for underlying event loops. Node. js is also one of them. Learning and analyzing the libev library helps you understand the underlying working principles of node. js. You can also learn from libev's design ideas. This article is a summary of some recent libev source code.

Libev example

First, let's take an example to see how libev is used.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
// a single header file is required#include <ev.h>#include <stdio.h> // for puts// every watcher type has its own typedef'd struct// with the name ev_TYPEev_io stdin_watcher;ev_timer timeout_watcher;// all watcher callbacks have a similar signature// this callback is called when data is readable on stdinstatic voidstdin_cb (EV_P_ ev_io *w, int revents){  puts ("stdin ready");  // for one-shot events, one must manually stop the watcher  // with its corresponding stop function.  ev_io_stop (EV_A_ w);  // this causes all nested ev_run's to stop iterating  ev_break (EV_A_ EVBREAK_ALL);}// another callback, this time for a time-outstatic voidtimeout_cb (EV_P_ ev_timer *w, int revents){  puts ("timeout");  // this causes the innermost ev_run to stop iterating  ev_break (EV_A_ EVBREAK_ONE);}intmain (void){  // use the default event loop unless you have special needs  struct ev_loop *loop = EV_DEFAULT;  // initialise an io watcher, then start it  // this one will watch for stdin to become readable  ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ);  ev_io_start (loop, &stdin_watcher);  // initialise a timer watcher, then start it  // simple non-repeating 5.5 second timeout  ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.);  ev_timer_start (loop, &timeout_watcher);  // now wait for events to arrive  ev_run (loop, 0);  // break was called, so exit  return 0;}

This is an example of the libev official website documentation. The steps for using libev are clear. Starting from main, we can find that the Code mainly does the following:

  • Obtain the ev_loop instance. Ev_loop, the name indicates an event loop, which is also the main organizer of our code.

  • Create and initialize watcher. Libev defines a series of watcher, which is responsible for a specific type of events. You can use the ev_type_init function to create a watcher instance (type is a watcher type, such as Io and timer ). In this example, I/O and timer watcher are created respectively, and corresponding callback functions are bound. When an event of interest occurs, the corresponding callback function will be called.

  • Register watcher in ev_loop. Generally, the ev_type_start function can be used. After successful registration, watcher is associated with the loop. When an event of interest is detected in the loop, the related watcher will be notified.

  • Start the event loop. That is, the ev_run function. After the event loop starts, the current thread/process will be blocked until the loop ends.

In the above example, the ev_break function in the two callback functions is the place where the loop is terminated. When the time-out period is 5.5 seconds or a standard input event occurs, the system enters the corresponding callback function, terminates the event loop, and exits the program.

How libev works

In general, libev actually implements the Reactor mode. It mainly includes the following roles: watcher, ev_loop, and ev_run.

Watcher

Watcher is the Event Handler in the Reactor. On the one hand, it provides a unified Calling Interface (differentiated by type) to the event loop; on the other hand, it is the injection port of external code and maintains specific watcher information, such: the bound callback function, watcher priority, and whether to activate the function.

In ev. h, we can see various watcher definitions, such as ev_io and ev_timer. The public attributes of watcher are defined as follows:

1234567
/* shared by all watchers */#define EV_WATCHER(type)         \ int active; /* private */           \ int pending; /* private */          \ EV_DECL_PRIORITY /* private  int priority; */       \ EV_COMMON /* rw  void *data; */             \ EV_CB_DECLARE (type) /* private */

The macro definition is as follows:

123
# define EV_DECL_PRIORITY int priority;# define EV_COMMON void *data;# define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents);
  • Active: indicates whether the current watcher is activated. Ev_type_start: Call the rear position, and reset after ev_type_stop is called;

  • Pending: indicates that the current watcher event is ready for processing. The pending value is actually the subscript of the Current Watcher in the pendings queue;

  • Priority: the priority of the Current Watcher;

  • Data: an additional data pointer to carry additional required data in watcher;

  • CB: the callback function definition after an event is triggered.

Add additional attributes to the watcher. Developers can select a specific type of watcher as needed, create an instance, initialize the instance, and register the instance to the loop. Libev defines several types of watcher. Each type of watcher is responsible for solving problems in a specific field (such as io, timer, signal, etc.) and can be used in ev. h.

Ev_loop

Ev_loop is a Reactor role, which is the context environment of the event loop. It is like a bamboo that Concatenates the previous watcher instance like a sugar gourd.

Ev_loop Definition

Ev_loop is defined in ev. c as follows:

12345678
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};

Ev_vars.h defines various ev_loop attributes. In ev_wrap.h, various macros related to loop are defined, and most of the Code is operated in the form of macros.

Watcher Management

In ev_loop, watcher stores data by type. For example, io loop> anfds, timer loop> timers. After activating watcher, ev_TYPE_start adds it to the corresponding storage structure (the specific implementation is described later in the watcher article for further analysis ).

In the event loop, watcher with event readiness is selected and saved to ev_loop. The ready watcher is maintained mainly by loop-> pendings and loop-> pendingcnt (as shown in ). These two items are two-dimensional arrays, and the first dimension is the priority. Pendings stores ANPENDING instances. The latter is used to maintain the watcher pointer, while pendingcnt stores the number of pendings elements corresponding to the priority. Each ready watcher also has a pending field to record its subscript in the pendings list, so that watcher can easily find its position in the pendings list, this is very helpful for delete operations.

After an event loop ends, the ready watcher is triggered based on the priority.

Bool ev_run (loop, flag)

The ev_run function is the engine used to execute the event loop, that is, the select method in Reactor mode. You can enable an event loop by passing an ev_loop instance to the ev_run function. Ev_run is actually a huge do-while loop, during which various watcher events registered in the loop will be checked. If an event is ready, the corresponding watcher is triggered. This cycle continues until ev_break is called or there is no active watcher. Of course, you can also control loop blocking by passing flags such as EVRUN_NOWAIT or EVRUN_ONCE.

The ev_run work content is described in detail in the API of the official documentation. It helps you understand ev_run through the documentation. The specific code is a bit long and will not be pasted here. If you are interested, you can view the ev_run implementation code in ev. c. Remove the condition check and unnecessary code, as shown in the main process.

As you can see, the main task of ev_run is to check events on various types of watcher by watcher, and add the ready watcher to the pending data structure through the ev_feed_event function. Finally, call ev_invoke_pending to trigger watcher in pending. After completion, the system checks whether there are active watcher and whether ev_break has been called, and then determines whether to continue the next round of loop.

Summary

In general, the structure design of libev is still very clear. If the main cycle ev_run is the trunk of the libev tree, it is powerful. A large number of watcher trees are the leaves of the tree, and the loop context ev_loop is the branches connecting the trunk and leaves, their division of labor and responsibilities are quite clear. As the backbone of the big tree, the ev_run code is very stable and clean, basically not mixed with the logic code of external developers. As the watcher of leaves, it is clearly positioned to focus on its own field and only solve a certain type of problem. There is not much interference and coupling between different watcher and the main loop, which is why libev can be flexibly expanded. The ev_loop function of the branches in the middle is self-evident. It is precisely because of its reconciliation in the middle that the first two buddies can live with such personality.

Here, the main architecture of libev is clear, but it seems that the key code related to performance has not yet been seen. Unlike the main code, these codes are mostly hidden in the implementation of specific logic, that is, watcher. Although watcher interfaces are similar, different watcher interfaces have different underlying data structures and processing policies. In the following article, we will explore the design and implementation of several watcher commonly used in libev.

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.