Jun 01, 2021 Article blog
This article focuses on the
Libevent
library and, by the way, introduces
I/O库
as a whole.
When dealing with these three types of events, we usually need to consider the following three issues:
I/O
multiplexing methods, such as
Solaris
dev/poll
file,
FressBSD
kqueue
mechanism,
Linux
epoll
system calls
Fortunately, the open source community provides a number of excellent
I/O
framework libraries that not only solve these problems, allowing developers to focus entirely on the logic of the program, but also on stability, performance, and more.
Libevent
is one of the relatively lightweight framework libraries.
I/O
framework library encapsulates lower-level system calls in the form of library functions, providing applications with a more user-friendly set of interfaces. T
hese library functions tend to be more reasonable, efficient, and robust than the same functions that programmers implement themselves.
Because they have stood the test of high voltage in real network environment, as well as the test of time.
The implementation principles of the various
I/O
framework libraries are basically similar, either in
Reactor
mode or
Procator
mode (high-performance server program framework - two efficient event handling modes), or both.
For example, the
Reactor
I/O
framework library contains several components:
Handle
handle
EventDemultiplexer
Eventhandler
ConcreteEventHandler
the specific event processor
Reactor
(Recommended tutorial: Linux tutorial)
The relationship between these components is illustrated below:
I/O
framework library, i.e.
I/O
events, signals, and timing events, are collectively referred to as event sources. A
n event source is usually bound to a handle. T
he purpose of a handle is that when the kernel detects a ready event, it notifies the application of the event through the handle.
In
Linux
environment, the handle for
I/O
event is a file descriptor, and the handle for a signal event is a signal value.
I/O
reuse techniques.
I/O
framework library typically encapsulates the various
I/O
multiplexing system calls supported by the system as a unified interface, called an event multiplexer. T
he event multiplexer's
demultiplex
method is the core function of waiting for an event, which
select
such as select,
poll
epoll_wait
In addition, the event multiple distributor implements
register_event
and
remove_event
methods for callers to add events to the event multiple distributor and remove events from the event multiple distributor.
handle_event
callback functions that are executed in an event loop. T
he event processor provided by
I/O
framework library is usually an interface that users need to inherit to implement their own event processor, the specific event processor. T
herefore, callback functions in the event processor are generally declared as required functions to support the user's extension. I
n addition, the event processor typically provides a
get_handle
method that returns the handle associated with the event processor. S
o what does the event processor have to do with the handle? W
hen the time multiple distributor detects an event, it notifies the application through a handle.
Therefore, we must bind the event processor to the handle in order to get the correct event processor when the event occurs.
handle_events
This method executes an event loop.
It repeats the process of waiting for an event and then processing the event processor for all ready events in turn.
register_handler
This method calls the event multiple distributor's
register_event
method to register an event in the event multiple distributor.
-
remove_handler
This method calls the
remove_event
method of the event multiple distributor to register an event in the event multiple distributor.
The working sequence of the I/O framework library is as follows:
Libevent is a high-performance I/O framework library for the open source community with the following features:
(Recommended micro-class: Linux micro-class)
Here's a
“Hello World”
program implemented with the
Libevent
library.
#include <event2/event.h>
void signal_cb(int fd, short event, void *argc)
{
struct event_base* base = (event_base*)argc;
struct timeval delay = {2, 0};
printf("Caught an interrupt signal; exiting cleanly in two seconds....\n");
event_base_loopexit(base, &delay);
}
void timeout_cb(int fd, short event, void* argc)
{
printf("timeout\n");
}
int main(int argc, char const *argv[])
{
struct event_base* base = event_base_new();
struct event* signal_event = evsignal_new(base, SIGINT, signal_cb, base);
event_add(signal_event, NULL);
timeval tv = {1, 0};
struct event* timeout_event = evtimer_new(base, timeout_cb, NULL);
event_add(timeout_event, &tv);
event_base_dispatch(base);
event_free(timeout_event);
event_free(signal_event);
event_base_free(base);
return 0;
}
The above code, while simple, basically describes the main logic of
Libevent
library:
event_base_new
function to create a
event_base
object.
A
event_base
is equivalent to one
Reactor
instance.
Reactor
instances to which they belong.
evsignal_new
and
evtimer_new
are used to create signal transaction processors and timing event processors, respectively.
They are defined as follows:
event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
#define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg))
It can be seen that their unified portal is
event_new
function, the function used to create a common event processor, as defined as follows:
event_new (struct event_base base, evutil_socket_t fd, short events, void (evutil_socket_t, short, void), void arg), where the base parameter specifies the row
thereinto:
base
parameter specifies
Reactor
to which the newly created event processor belongs.
fd
parameter specifies the handle associated with the event processor.
When you create an
I/O
event processor, you should pass a file descriptor to the
fd
parameter, when you create a signal event processor, you should pass a signal value to
fd
parameter, such as
SIGINT
in the previous instance code, and when you create a timed event processor, you should pass
-1
to
fd
parameter.
events
parameter specifies the event type, as defined below:
#define EV_TIMEOUT 0x01 /*定时事件*/
#define EV_READ 0x02 /*可读事件*/
#define EV_WRITE 0x04 /*可写事件*/
#define EV_SIGNAL 0x08 /*信号事件*/
#define EV_PERSIST 0x10 /*永久事件*/
/*边缘触发事件,需要I/O复用系统调用支持,比如epoll */
#define EV_ET 0x20
In the code above,
EV_PERSIST
is to automatically call the
event_add
function again to this
event
when the event is triggered.
cb
parameter specifies the callback function corresponding to the target event, which is equivalent to the event processor
handle_event
method.
arg
is the argument
Reactor
passes to the callback function.
event_new
function
event
an object of the event type, the event processor of
Libevent
when it succeeds.
Libevent
uses the word
“event”
to describe the event processor, not the event, so the convention is as follows:
event
of the event structure lift type, have many other members, such as callback functions, in addition to the two features (handles and event types) that the event must have
event_base
event_add
function, add the event processor to the registered event queue, and add the event corresponding to the event processor to the event multiple distributor.
even_add
function is equivalent to
register_handler
method in
Reactor
event_base_dispatch
function to execute the event loop
*_free
of _free
(Recommended course: That's what Linux should learn)
include/event2
The directory was introduced after
Libevent
motherboard was upgraded to 2.0 and is available to applications such as
event.h
header files as core functions,
http.h
header files with
HTTP
protocol-related services,
rpc.h
header files with remote procedure call support.
include/event2
directory
Libevent
all of which have file names in the form of
*-internal.h
compat/sys
T
here is only one file in the directory ----
queue.h
It encapsulates the underlying data structure across platforms, including one-way lists, bidirectional lists, queues, tail queues, and loop queues.
sample
directory.
Some sample code is provided
test
directory.
Provide a one-time amount test code
WIN32-Code
。
Provides some specialized code on the
Windows
platform.
event.c
file.
The overall framework of the file time
Libevent
is primarily related to the operations of the two structures,
event
and
event_base
debpoll.c
kqueue.c
evport.c
select.c
win32select.c
poll.c
and
epoll.c
files. T
hey encapsulate the following
I/O
reuse mechanisms:
/dev/poll
kqueue
event ports
POSIX select
Windows select
poll
and
epoll
The main contents of these files are similar and are specific implementations of interface functions defined by the structure
eventop
minheap-internal.h
This file implements a heap of events to provide support for timed events.
signal.c
Provides support for signals.
It is also a concrete implementation of interface functions defined by the structure
eventop
evmap.c
file: It maintains a mapping relationship between a handle (file descriptor or signal) and a time processor
event_tagging.c
Provides a function to add tag data to the buffer, such as a positive number, and to read tag data from the buffer
event_iocp
files: Support for
Windows IOCP
(Input/Output Finish Port, Input and Output Completion Port) is available
buffer*.c
files: Provides control over network
I/O
buffering, including input and output data filtering, transfer rate limiting, protection of application data using
SSL
Sockets Layer protocol, and zero-copy file transfer.
evthread*.c
file: Provides support for multithreaded
listener.c
Encapsulates the operation of listening to
socket
including listening for connections and accepting connections
logs.c
file.
It is
Libevent
log file system
evutil.c
evutil_rand.c
strlcpy.c
and
arc4random.c
files: provide basic operations such as generating random numbers, getting
socket
address information, reading files, setting
socket
properties, and so on
evdns.c
http.c
and
evrpc.c
address information: support for
DNS
protocols,
HTTP
protocols, and
RPC
(Remote Procddure Call, Remote Procedure Call), respectively
epoll_sub.c
file, which is not in use
Of the entire source code, four
event-internal.h
include/event2/event_struct.h
event.c
and
evmap.c
are the most important.
They define
event
and
event_base
structures and implement the operations associated with both structures.
Here's a look at
Libevent
the high-performance
I/O
framework library in
Linux
and hopefully it'll help.