Introduction to Libevent Event-driven library for cross-platform

Source: Internet
Author: User
Tags data structures epoll unix domain socket

Original address:


http://blog.csdn.net/eroswang/article/details/4206284




Recently because of work reasons, the pressure of the background server is getting bigger and larger, need to rebuild the server in the background of the project, reconstruct the communication module of the existing server, and increase its performance.

the background that libevent produced

Usually when we set up the processing model of the server, it is mainly the following centralized model;

  (1)     a new connection comes in and generates a process processing with fork (). &NBSP
   (2)    a new connection comes in and generates a Thread processing with pthread_create (). &NBSP
   (3)    a new connection in, dropped into event-based Array, with Main Process to nonblocking The way to handle all I/O.
These three methods of course also have their own disadvantages: the problem with the
  fork () is that each Connection comes in with too much cost, and if there are too many concurrent connections at the same time, there is a large number of simultaneous processes, and the switching overhead between processes is very high, while for the old kernel ( Linux) can have avalanche effects. &NBSP
  multi-thread problem is that Thread-safe and deadlock problems are difficult to solve, and memory-leak problems to deal with, this problem for many programmers is a nightmare, In particular, server programs for continuous servers are not acceptable. If the way to use event-based is hard to write, especially to note that the event must be nonblocking, so you will need to do buffering problems, and Multi-thread will encounter Memory-leak The problem is more serious here. There is no way to use all CPU resource on a multiple-CPU system.  

     for the problems above, the common approach is to Poll: When a process finishes processing a Connection, does not die directly, and continues back to accept () the state continues to process , but this will encounter memory-leak problems, so the person in this way will often add "handled N Connection after death, by the Parent process again fork () a new". The most famous example is the Apache 1.3 server, you can refer to the implementation of its source code.  hread-safe problems can be found in other Thread-safe Library for direct use. Memory-leak questions can be tried through the garbage Collection Library analysis. This pattern is used for the Thread MPM of Apache 2.0.
     However, currently efficient servers prefer to use event-based, on the one hand is not created process/thread caused by the overhead, on the other hand is not required through Shared Memory or mutexes exchange information between different process/thread. However, some of the complexities of event-based are that
 select () and poll () are inefficient, resulting in a high cost of determining "what event happens" at a time when the BSD support Kqueue (), Linux The support for Epoll (), Solaris support/dev/poll is resolved after the completion of the port on the Windows platform. But neither of these sets of Function is Standard, so it has to be changed on a different platform.

For non-blocking IO models, because of the nonblocking, it is necessary to buffering when write () or send () is full. Because of nonblocking, you cannot use fgets () or other similar function, so you need to carve a nonblocking fgets (). But the information that the user throws is not guaranteed to have one line at a read () or recv (), so he wants to do his own buffering. In fact, these three things have been libevent in the Library.

Libevent is an event-triggered network library, suitable for Windows, Linux, BSD and other platforms, internal use of Select, Epoll, Kqueue and other systems to invoke the management event mechanism. The famous PHP cache for Apache Memcached is also said to be libevent based, and libevent can be used on a cross-platform, if you are going to develop an application that needs to support more than two of the platforms listed above, then strongly recommend that you use this library , even if your application only needs to support one platform, it is also advantageous to choose Libevent, because it can switch the underlying event-driven mechanism according to the compile/run environment, which can not only give full play to the performance of the system, but also increase the portability of the software. It encapsulates and isolates the event-driven underlying mechanism, which also provides read-write timeouts, timers, and signal callbacks, in addition to the general file descriptor read and write operations, and also allows for different priorities for events, and the current version of Libevent also provides asynchronous encapsulation of DNS and HTTP protocols, All this makes this library especially suited to the development of event-driven applications.

The following is an introduction to the Libevent implementation Framework

Please refer to: Libevent official website: http://www.monkey.org/~provos/libevent/
Better Documentation:
http://unx.ca/log/category/libevent/

http://tb.blog.csdn.net/TrackBack.aspx?PostId=1808095

The code structure of the Libenvent library can be roughly divided into several modules:
Event-Handling Framework
Event Engine Module
Buffer Management Module
Signal Processing Module

1. Event-Handling framework

1.1 Event_init () initialization
First of all, to introduce the Event_base object:

struct Event_base {
    const struct eventop *evsel;
    void *evbase;
  ;   int event_count;       /* counts number of total events */
 & nbsp;  int event_count_active; /* counts number of active events */
    
    int event_gotterm;  & nbsp;  /* Set to terminate loop */
        
    /* Active Event Management */
    struct event_list **activequeues;
    int n Activequeues;
    struct event_list eventqueue;
    struct Timeval event_tv;
    rb_head (Event_tree, event) Timetree;
};

The

    event_base Object consolidates some of the global variables that event processing,  role is the "explorer" of the event object, and he includes the Events engine function object (Evsel, evbase), Current row Events list (Event_count, event_count_active, EventQueue), Global termination signal (EVENT_GOTTERM), Active events list (avtivequeues), Event queue tree ( Timetree) ... Create Event_base Object,  Select   Current OS supported event engine (Epoll, poll, select ...) and initialize, create the global signal Queue (SIGNALQUEUE), the memory allocation of the active queue (default to 1 according to the number of priority set).
 1.2 event_set () event definition
    event_set to set the event object, including the owner Event_base object, FD, event (ev_read| Ev_write), returns functions and parameters, and event precedence is the intermediate level (CURRENT_BASE->NACTIVEQUEUES/2) of the current event_base. The definition of the event object is shown below:

struct Event {
Tailq_entry (event) Ev_next;
Tailq_entry (event) Ev_active_next;
Tailq_entry (event) Ev_signal_next;
Rb_entry (event) Ev_timeout_node;
struct Event_base *ev_base;
int ev_fd;
Short ev_events;
Short Ev_ncalls;
Short *ev_pncalls; /* allows deletes in callback * *
struct Timeval ev_timeout;
int ev_pri; /* Smaller numbers are higher priority * *
void (*ev_callback) (int, short, void *arg);
void *ev_arg;
int ev_res; /* Result passed to event callback * *
int ev_flags;
};

1.3 Event_add () event add:
int Event_add (struct event *ev, struct Timeval *tv)
This interface has two parameters, the first is the event to be added, and the second parameter as the timeout value for the event (timer). If the value is not NULL, adding the timeout event (Ev_timeout) to the time Queue tree (timetree) while adding this event is handled according to the event type as follows:
Ev_read => evlist_inserted => eventqueue
Ev_write => evlist_inserted => eventqueue
Ev_timeout => evlist_timeout => timetree
Ev_signal => evlist_signal => signalqueue
1.4 Event_base_loop () event handling main loop
Here is the main loop of the event, and as long as the flags are not set to Evloop_nonblock, the function loops over the event/handle events.
During each loop, the current triggering (active) event is handled:
(a). Detect whether there is currently a signal processing (gotterm, Gotsig), these are global parameters, not suitable for multithreading
(b). Time update, find the nearest time event, get relative timeout event TV
(c). Invoke the event engine dispatch wait Event trigger, timeout value is TV, triggering event added to Activequeues
(d). Handling active events, calling caller callbacks (EVENT_PROCESS_ACITVE)
2. Event Engine module:

Linux has a variety of I/O multiplexing mechanism,. To handle multiple-channel event monitoring, Common Epoll, poll, select, ranked by priority:
Evport
Kqueue
Epoll
Devpoll
Rtsig
Poll
Select
When Event_init () selects the event engine, it is detected from the top down by priority, and if the detection succeeds, the current engine is selected. Each engine needs to define several handler functions, taking Epoll as an example:

struct Eventop epollops = {
"Epoll",
Epoll_init,
Epoll_add,
Epoll_del,
Epoll_recalc,
Epoll_dispatch,
Epoll_dealloc
};

3. Buffer Management module:

Libevent defines its own buffer management mechanism Evbuffer, supports the read/write functions of various types of data, including indefinite long strings, memory in the buffer using pre-allocation/on-demand combination, It is easier to manage multiple data structures mapped to memory buffer.
Need to pull out to introduce the Evbuffer_expand () function, when the internal memory is not enough, the need for expand, here, in a pre-configured way, if you need the length of <256 bytes, 256 bytes in advance, while the memory multiplied, until the required length.
4. Signal Processing Module

Signal processing is proposed separately, mainly libevent signal processing is relatively lightweight, so that a good fusion to the event mechanism.
Singal module Initialization (evsignal_init), a UNIX domain socket (pipe) was created as an internal messaging bridge:

if (Socketpair (Af_unix, Sock_stream, 0, ev_signal_pair) = = 1)
Event_err (1, "%s:socketpair", __func__);
Fd_closeonexec (Ev_signal_pair[0]);
Fd_closeonexec (ev_signal_pair[1]);
Fcntl (Ev_signal_pair[0], F_SETFL, O_nonblock);
Event_set (&ev_signal, ev_signal_pair[1], Ev_read,
EVSIGNAL_CB, &ev_signal);
Ev_signal.ev_flags |= evlist_internal;

Evsignal_add (), adding signal events, associated signal processing methods (sigaction)
In the actual running process, if a singal occurs, the corresponding signal processing method is called, write a character to pipe
At the same time, the other end of the pipe is activated, adding signals to singalqueue, evsignal_process processing signal callbacks in the event loop.

The concrete use method of Libevent library

Write a simple time server as an example: when you connect to it, the Server side provides it directly and then ends the connection. Event_init () represents the variable used by the initialization libevent. Event_set (&ev, S, Ev_read | Ev_persist, Connection_accept, &ev) puts s This File Description into the EV (the first parameter and the second parameter) and tells you to call when the event (Ev_read of the third argument) occurs Connec Tion_accept () (fourth parameter), the EV is dropped in as a parameter when calling (the fifth parameter). The ev_persist says that when the call goes in, don't take the event away (keep it in the event Queue), which can be compared to the code in the Connection_accept () internal registration Connection_time (). Event_add (&ev, NULL) registers the EV into the event queue, and the second parameter specifies the Timeout time, which is set to NULL to ignore the setting.

Note: This code from the network, although very rough, but the use of libevent has been clearly explained.

Attaching source code: Use method

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <event.h>
#include <stdio.h>
#include <time.h>

void Connection_time (int fd, short event, struct event *arg)
{
Char buf[32];
struct TM T;
time_t now;

Time (&now);
Localtime_r (&now, &t);
Asctime_r (&t, buf);

Write (FD, buf, strlen (BUF));
Shutdown (FD, SHUT_RDWR);

Free (ARG);
}

void connection_accept (int fd, short event, void *arg)
{
/* For debugging * *
fprintf (stderr, "%s (): FD =%d, event =%d./n", __func__, FD, event);

/* Accept a new connection. */
struct sockaddr_in s_in;
socklen_t len = sizeof (s_in);
int ns = Accept (FD, (struct sockaddr *) &s_in, &len);
if (ns < 0) {
Perror ("accept");
Return
}

/* Install time server. */
struct Event *ev = malloc (sizeof (struct event));
Event_set (EV, NS, Ev_write, (void *) connection_time, Ev);
Event_add (EV, NULL);
}

int main (void)
{
/* Request socket. */
int s = socket (pf_inet, sock_stream, 0);
if (s < 0) {
Perror ("socket");
Exit (1);
}

/* BIND () */
struct sockaddr_in s_in;
Bzero (&s_in, sizeof (s_in));
s_in.sin_family = af_inet;
S_in.sin_port = htons (7000);
S_IN.SIN_ADDR.S_ADDR = Inaddr_any;
if (Bind (s, (struct sockaddr *) &s_in, sizeof (s_in)) < 0) {
Perror ("bind");
Exit (1);
}

/* Listen () * *
if (Listen (S, 5) < 0) {
Perror ("Listen");
Exit (1);
}

/* Initial libevent. */
Event_init ();

/* Create event. */
struct event ev;
Event_set (&ev, S, Ev_read | Ev_persist, Connection_accept, &ev);

/* ADD event. */
Event_add (&ev, NULL);

Event_dispatch ();

return 0;
}

Writing nonblocking network program usually deals with buffering problems, but it's not easy to write, mainly because read () or recv () does not guarantee that a single line of weight can come in at one time.

In the libevent provide a very good Buffer Library can be used, complete instructions in the man event can be seen, the most commonly used should be Evbuffer_add (), Evbuffer_readline () the two functio N, other know existence is OK, need to see the detailed usage again.

The following directly provide LIBEVENT-BUFF.C as an example, compiled to see the results of the implementation, and then look back to the source code should have a feeling:

#include <sys/time.h>
#include <event.h>
#include <stdio.h>

void Printbuf (struct evbuffer *evbuf)
{
for (;;) {
Char *buf = evbuffer_readline (EVBUF);
printf ("* buf =%p, the string =/"/e[1;33m%s/e[m/"/n", buf, BUF);
if (buf = NULL)
Break
Free (BUF);
}
}

int main (void)
{
struct Evbuffer *evbuf;

Evbuf = Evbuffer_new ();
if (evbuf = = NULL) {
fprintf (stderr, "%s (): Evbuffer_new () failed./n", __func__);
Exit (1);
}

/* ADD "Gslin" into buffer. */
U_char *buf1 = "Gslin";
printf ("* Add/"/e[1;33m%s/e[m/"./n", BUF1);
Evbuffer_add (Evbuf, Buf1, strlen (BUF1));
Printbuf (EVBUF);

U_char *buf2 = "is Reading./nand it at Home./nlast.";
printf ("* Add/"/e[1;33m%s/e[m/"./n", buf2);
Evbuffer_add (Evbuf, Buf2, strlen (BUF2));
Printbuf (EVBUF);

Evbuffer_free (EVBUF);
}

The

Final Event_dispatch () represents entering the event loop and entering the callback function execution when any File Description in the Queue occurs.

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.