Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Brief Analysis of Libevent

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)06/01 Report--

Introduction

Libevent is a lightweight open source high-performance event-driven network library and a typical Reactor model. Its main features are event-driven, high-performance, cross-platform, unified event source and so on.

There are many blogs related to the source code analysis of libevent on the Internet, and I also rely on the Internet in the process of learning. Therefore, here, many specific implementation parts of libevent are not introduced, mainly from the relevant data structure level. For reference only.

Event structure

The event handling type in libevent is the event structure type. The event structure encapsulates handles, event types, callback functions, and other necessary flags and data, which is the core of the entire libevent library.

The structure is defined as follows:

Struct event {/ * * ev_next, ev_active_next are two-way linked list node pointers * they are the fields used by libevent to manage events in different event types and in different periods * * libevent uses a two-way linked list to save all registered IO and signal events * ev_next is the location of the IO event in the linked list Call this linked list registered event list * ev_active_next: libevent puts all activation events into the linked list active list, and then traverses active list * to perform scheduling. Ev_active_next indicates the location of event in active list * / TAILQ_ENTRY (event) ev_next. TAILQ_ENTRY (event) ev_active_next / * * _ ev is a consortium. All IO events with the same descriptor are concatenated into a * tail queue by ev.ev_io.ev_io_next members, which is called IO event queue. All signal events with the same signal value are concatenated into a tail queue by ev.ev_signal.ev_signal_next *, which is called signal event queue. When the ev.ev_signal.ev_ncalls member specifies the time, how many times does Reactor * need to execute the callback function corresponding to this event? ev.ev_signal.ev_pcalls is either NULL or ev.ev_signal.ev_ncalls * / union {struct {TAILQ_ENTRY (event) ev_io_next; struct timeval ev_timeout;} ev_io Struct {TAILQ_ENTRY (event) ev_signal_next; short ev_ncalls; short * ev_pcalls;} ev_signal;} _ ev / * * ev_timeout_pos is a consortium that is only used for timing event handlers. In the old version of libevnet, the minimum heap management * timer was used, but developers believe that sometimes simple linked list management is more efficient. So the concept of "universal timer" is introduced in the new version. These timers are not stored in the time heap, but in the tail queue, which we call universal timer queues. * for a generic timer, the ev_next_with_common_timeout member in ev_timeout_pos indicates the position of the timer * in the queue; for other timers, the min_heap_idx member indicates the position of the timer in the time heap. Whether a * timer is a universal timer depends on the size of its timeout value. For more information, please see the is_common_timeout function in event.c. * / union {TAILQ_ENTRY (event) ev_next_with_common_timeout; int min_heap_idx;} ev_timeout_pos; / / if it is a timeout event, the ev_timeout timeout value struct timeval ev_timeout; / / ev_base: the reactor instance to which the event belongs, which is an event_base structure struct event_base * ev_base / / for the IO event, the bound file descriptor, and for the signal event, the bound signal int ev_fd / * * ev_events: the type of event that event follows. It can be the following three types: * IO event: EV_WRITE / EV_READ * timing event: EV_TIMEOUT * signal: EV_SIGANL * Auxiliary option: EV_PERSIST, indicating that it is a permanent event * / short ev_events / / events can be combined using the "|" operator. Signals and IO events cannot be set at the same time. / / when the event is ready for execution, the number of times ev_callback is called, usually 1 short ev_ncalls; / / pointer, pointing to ev_ncalls or NULL short * ev_pncalls; / / allows deletes in callback int ev_pri / / smaller numbers are higher priority / / ev_callback:event callback function, called by ev_base, executes the event handler, which is a function pointer / / where fd corresponds to ev_fd, events corresponds to ev_events, arg corresponds to ev_arg void (* ev_callback) (int, short, void* arg); / / void* indicates that it can be of any type, and void* ev_arg is specified when setting event. / / record the type int ev_res; / / result passed to event callback / * * libevent of the current activation event used to mark the field of event information, indicating the current status * / int ev_flags;}

As you can see from the definition of the event structure, event encapsulates handles, callback functions, and event types. Includes the index position of the event in the corresponding linked list or time heap. The macro TAILQ _ ENTRY is the node type of the tail queue, which is defined as:

# define TAILQ_ENTRY (type)\ struct {\ struct type * tqe_next;\ / * next element * / struct type * * tqe_prev;\ / * address of the previous element * /}

Whenever an event event changes to a ready state, libevent moves it into active event list [priority], where priority is the priority of event; then libevent selects the ready event according to its scheduling policy and calls its cb_callback () function to perform event handling.

Event handling Framework event_base

The structure event_base is the Reactor of libevent, which is declared as follows:

Struct event_base {/ * selects a back-end IO reuse mechanism when initializing Reactor, and records in the following field * / const struct eventop * evsel; / * points to the data actually stored by the IO reuse mechanism, which initializes * / void * evbase through the init function of the evsel member. / * the back-end processing mechanism pointing to the signal. At present, only one data structure used by the signal processor * / const struct eventop * evsigsel; void * evsigbase; / * is defined in the signal.h file, which encapsulates a socketpair-created pipeline for communication between signal processing functions and event demultiplexers. * / struct evsig_info sig; / * number of all events and activation events added to the event_base * / int event_count; / *

< counts number of total events */ int event_count_active; /**< counts number of active events */ /* 是否执行完活动事件队列上的剩余任务之后就退出事件循环 */ int event_gotterm; /**< Set to terminate loop once done * processing events. */ /* 是否立即退出事件循环,而不管是否还有任务需要处理 */ int event_break; /**< Set to exit loop immediately */ /* 活动事件队列数组。索引越小的队列,优先级越高。高优先级的活动事件队列中的事件处理器将被优先处理*/ struct event_list **activequeues; /* 活动事件队列数组的大小,即该event_base一共有nactivequeues个不同优先级的活动事件队列*/ int nactivequeues; /*是否应该启动一个新的事件循环*/ int event_continue; //目前正在处理的活动事件队列的优先级 int event_running_priority; //事件循环是否已经启动 int running_loop; /** Deferred callback management: a list of deferred callbacks to * run active the active events. */ TAILQ_HEAD (deferred_cb_list, deferred_cb) deferred_cb_list; //文件描述符和IO事件之间的映射关系表 struct event_io_map io; /*信号值和信号事件之间的映射关系表*/ struct event_signal_map sigmap; /*注册时间队列,存放IO事件处理器和信号事件处理器*/ struct event_list eventqueue; /*时间堆*/ struct min_heap timeheap; //系统管理时间的一些成员 struct timeval event_tv; struct timeval tv_cache;}; 其中: evsel和evbase这两个字段的额设置可能会让人迷惑,可以将其看作是类和静态函数的关系,比如添加事件时的调用行为:evsel->

Add (evbase, ev), which actually performs the operation is evbase, which is equivalent to class::add (instance, ev), and instance is an instance of class. Evsel points to one of the global variables static const struct eventop * eventops []. Eventops [] contains several global instance objects, such as select,poll,kequeue, epoll, and so on. Evbase is actually an eventop instance object.

The eventop structure is a series of function pointers defined as follows:

Struct eventop {const char* name; void * (* init) (struct event_base*); / / initialize int (* add) (void *, struct event *); / / register event int (* del) (void *, struct event *); / / delete event int (* dispatch) (struct event_base*, void *, struct timeval *); / / event distribution void (* dealloc) (struct event_base*, void *) / / Log out, release resources / / set if we need to reinitialize the event_base int need_reinit;}

In libevent, each implementation of the IO event distribution mechanism must provide these five function interfaces.

The event main loop is mainly accomplished through the event_base_loop () function. The code is as follows:

Int event_base_loop (struct event_base * base, int flags) {const struct eventop * evsel = base- > evsel; void * evbase = base- > evbase; struct timeval tv; struct timeval * tv_p; int res, done; / / clear time cache base- > tv_cache.tv_sec = 0 / / evsignal_base is a global variable, which is used to indicate the event_base instance if (base- > sig.ev_signal_added) evsignal_base = base; done = 0 to which signal belongs when processing signal / / event main cycle while (! done) {/ / check whether you need to jump out of the loop. The program can call event_loopexit_cb () to set the event_gotterm tag / / call event_base_loopbreak to set the event_break flag if (base- > event_gotterm) {base- > event_gotterm = 0; break } if (base- > event_break) {base- > event_break = 0; break;} / / you cannot use this interface for multi-threaded apps while (event_gotsig) {event_gotsig = 0; if (event_sigcb) {res = (* event_sigcb) () If (res = =-1) {errno = EINTR; return-1 } / / correct the system time. If the system is using a non-MONOTONIC time, the user may adjust the system time backward / / in the timeout_correct function, compare the last wait time with the current event, if / / the current time

< last wait time //表明时间有问题,这需要更新timer_heap中所有定时事件的超时时间 timeout_correct(base, &tv); //根据time heap中事件的最小超时时间,计算系统IO demultiplexer的最大等待时间 tp_p = &tv; if(!base->

Event_count_active &! (flags & EVLOOP_NONBLOCK) {timeout_next (base, & tv_p);} else {/ / still has unprocessed ready time, let IO demultiplexer return immediately without waiting / / as mentioned below, in libevent, low-priority ready events may not be processed immediately evutil_timerclear (& ready) } / / if no event is currently registered, exit if (! event_haveevents (base)) {event_debug ("% s: no events registered.", _ _ func__); return 1;} / / update last wait time and clear time cache gettime (base, & base- > event_tv); base- > tv_cache.tv_sec = 0 / / call system IO demultiplexer to wait for ready IO events, which may be epoll_wait, or select etc. / / in evsel- > dispatch (), ready signal event / IO event will be inserted into the active list res = evsel- > dispatch (base, evbase, tv_p); if (res =-1) return-1 / / assign the time cache to the current system time gettime (base, & base- > tv_cache); / / check the timer events in the heap, delete the ready timer event from the heap, and insert the timeout_process (base) into the active linked list / / call event_process_active () to activate the ready event in the linked list Call its callback function to perform event handling / / this function looks for the activation event list with the highest priority (the lower the priority value, the higher the priority) / and then processes all ready events in the linked list / / so low-priority ready events may not have to deal with if (base- > event_count_active) {event_process_active (base) in time. If (! base- > event_count_active & & (flags & EVLOOP_ONCE)) done = 1;} else if (flags & EVLOOP_NONBLOCK) done = 1;} / / cycle ends, empty time cache base- > tv_cache.tv_sec = 0; event_debug ("% s: asked to terminate loop.", _ _ func__); return 0 }

Unified event source, libevent unifies timer and signal events into the IO demultiplex mechanism of the system

Through socketpair to achieve. That is, a socket pair with two socket, one read and one write.

The read socket registers a read event in the event main loop instance. When the signal occurs, a character is written to the write socket, usually the signal value. At this time, there is a read event on the read socket, triggering the IO demultiplex read event, and then being processed together with the ordinary IO event.

The unity of timer and IO events. Because the IO mechanisms of the system, such as select () and epoll_wait (), allow the program to set a maximum waiting time, set the maximum waiting time of IO demultiplex according to the minimum timeout of all timer events, and then activate all ready timer events when IO returns, so that timer events are perfectly integrated into the IO mechanism of the system.

The unification of IO and signal. Because the emergence of signal is completely random to the process. So when signal occurs, instead of immediately calling the callback function of event to process the signal, we try to notify the IO mechanism of the system, let it return, and then uniformly deal with the IO event and timer.

The flow of the event main loop is as follows

1) start 2) adjust the system time or not 3) calculate the maximum wait time of the system IO demultiplexer according to the minimum timeout time of the event in the timer heap 4) update the last wait time and clear the time cache5) call the system O demultiplexer to wait for the ready I events6) check the activation flag of the signal, if set Check the active signal event and insert the event into the active list 7) insert the ready event into the active list 8) check the timer events in the heap, remove the ready timer event from the heap and insert it into the active list 9) handle the ready event in the active list according to priority and call its callback function to perform event handling (the lower the priority is, the higher the priority) 10) end

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report