Libevent Source Code Analysis (7) Analysis of--2.1.8--signal event processing mechanism

Source: Internet
Author: User
Tags bind data structures goto prepare signal handler versions
The signal binding mechanism simply means that the external signal is converted to an internal IO event for processing. Since the signal capture function is globally bound, there is no way to bind the IO event and file descriptor as an IO event, and libevent needs to implement the event triggering mechanism for the IO event, signal event, and timing event, so for the signal event, an intermediate relay is needed to achieve it. The signal event is converted to an IO event at the time of notification event_base, specifically, it is not possible to change the implementation of the global binding at signal binding, only to make changes on the notification Event_base: (1) libevent create a signal notification pipeline internally for internal transmission of captured signals (2) The two ends of the pipe are event_base and the signal capture function (3) binds one end of the pipe file descriptor and the callback function of the signal triggering event to a piece, registers to the Event_base, once the pipeline has the signal to come, the callback function will process the signal in the pipeline, therefore event_ Base requires only a background method to listen to this internal pipe to see if a signal event is coming. (4) When adding a signal event, the signal is bound to the global signal capture function, the processing logic in the signal capture function is that once the signal is captured, the signal is written to the internal notification pipeline, so that the signal capture function only need to write the signal to the pipeline, you can notify the event_base signal to occur.
Specifically, it is:

Signal capture function---write internal pipe, internal signal callback function, activate external signal callback function


Second, the Evsig_init evsig_init function is the initialization of internal signal events, Epoll, poll, select and other background methods in the initialization will perform internal signal event initialization, for the next signal event registration to prepare.
The main is to bind the signal event Base->sig.ev_signal[0] to the base, the Signal Association file descriptor;//Base->sig.ev_signal_pair is a file descriptor pair, one for reading data, One is used to write data;//where base->sig.ev_signal_pair[0], which is used to read the signal;//base->sig.ev_signal_pair[1] is used to write a signal, here is only bound 0, The base->sig.ev_signal_pair[0] int evsig_init_ (struct event_base *base) {/* * Ou will be bound only if there is really an external signal event registered, because this is the receiving end of the signal  R signal handler is going to write to one end of the socket * pair to wake up our event loop.
     The event loop then scans for * signals that got delivered. The///signal handle will be written to one of the socket pairs to wake the event loop, and//the event loop will check the signals of these transmissions//create a notification pipeline for internal use, and create a good file descriptor exists Ev_signal_pai R//below will parse if (evutil_make_internal_pipe_ (base->sig.ev_signal_pair) = =-1) {#ifdef _WIN32/* Make th is nonfatal on Win32, where sometimes people has localhost firewalled.
*/Event_sock_warn ( -1, "%s:socketpair", __func__);
#else Event_sock_err (1,-1, "%s:socketpair", __func__);
    #endif return-1; }//If presentThe original signal handle is released, and the null//BASE->SIG type is://typedef void (*ev_sighandler_t) (int);    /* Data structure for the default signal-handling implementation in SIGNAL.C *///struct Evsig_info {// 
     /* Event watching EV_SIGNAL_PAIR[1] *///monitor ev_signal_pair[1] events, i.e. internal signal pipeline monitoring Event//struct event ev_signal;
     /* Socketpair used to send notifications from the signal handler *//////////////internal signal pipe at both ends, one end of 0 is used to read the signal, 1 to write a signal
     evutil_socket_t ev_signal_pair[2]; /* True iff we ' ve added the Ev_signal event yet.
     *///If we have added ev_signal to true, that is, register an external signal event words//int ev_signal_added; /* Count of the number of signals we ' re currently watching.

      *///used to count the current event_base exactly how many signals//int ev_n_signals_added are monitored;  /* Array of previous signal handler objects before Libevent started//* messing with them. Used to restore old signal handlers. *///////#ifdef the original signal handle before the signal processing of the LibeventNt__have_sigaction//struct sigaction **sh_old;
     #else//ev_sighandler_t **sh_old; #endif//* Size of Sh_old.
     *////The maximum number of original signal handle//int sh_old_max;

    // };
    if (base->sig.sh_old) {mm_free (base->sig.sh_old);
    } base->sig.sh_old = NULL;

     Base->sig.sh_old_max = 0; Bind the signal event base->sig.ev_signal to base, the associated file description kerning base->sig.ev_signal_pair[0],//ev_signal_pair[0] is for reading, the event type is EV_ read| Ev_persist, including readable and persistent, event occurs//when the callback function is the EVSIG_CB function, the parameters of the callback function is base//Here is illustrated, Base->sig.ev_signal is an internal event, is used to notify when external signals occur Signal processing function, or//the external signal is actually converted to internal IO event to process///The next article Analysis Event_assign (&base->sig.ev_signal, base, Base->sig.ev _signal_pair[0], Ev_read |

     Ev_persist, EVSIG_CB, base);
     Set the event state of the signal event to evlist_internal, that is, internal events, distinguish internal and external events base->sig.ev_signal.ev_flags |= evlist_internal;
Set the signal event priority to 0, that is, the highest priority, the signal Event priority processing Event_priority_set (&base->sig.ev_signal, 0);
     Set the background processing method of the signal event, Evsigops//below to analyze the specific execution of the signal processing background method//static const struct Eventop Evsigops = {//"sig
     NAL ",//NULL,//Evsig_add,///Evsig_del,//NULL,//NULL,//0, 0, 0
     //     }; 

    The following analysis Evsig_add and Evsig_del functions//each time the signal event is registered, the Evsig_add is called to register the signal to event_base Base->evsigsel = &evsigops;
return 0; }




Three, Evutil_make_internal_pipe_ function
The main thing is to create an internal pipeline that serves as the notification pipeline between the external signal capture function and the internal IO event callback function.
/* Internal Function:set fd[0] and fd[1] to a pair of FDS such this writes on * fd[1] get read from fd[0].
 Make both FDS nonblocking and close-on-exec.
 * Return 0 on success,-1 on failure. ////////Intrinsics: Set fd[0] and fd[1] as file descriptor pairs so that you can read from fd[0], write from fd[1],//The two FDS modes are nonblocking and close-on-exec//close-on-exec: When the child process fork the parent process, it uses a write-time copy method to get the data space, heap, and stack copies of the parent process, including the file descriptor, when the fork succeeds, the same file descriptor in the parent-child process points to the same item in the file table (that is, the shared//file offset). This includes, of course, the socket created by the parent process, and then, normally called Exec to execute another program, this time//will replace the body of the child process with a new program, data, heap and stack, and so on, the saved file descriptor of course does not exist, we//will not be able to close the useless file descriptor, You typically use the fork child process to close the unused file descriptor directly in the child process, and//and then execute exec. However, in complex systems, it is sometimes difficult to fork a child process without knowing how many file descriptors have been opened, and//expect to be specified when a file descriptor is opened before the fork process: This descriptor is closed when the fork executes exec, that is, the specified//
Open mode is close-on-exec.  int Evutil_make_internal_pipe_ (evutil_socket_t fd[2]) {/* Making the second socket nonblocking is a bit subtle, Given that we ignore any eagain returns if writing to it, and you don ' t usally does that for a nonblocking so Cket. But if the kernel gives us eagain and then there' s no need to add any further data to the buffer, since the main thread was already either about to wake up and drain it
    , or woken up and in the process of draining it. *///Specify a second socket non-blocking has some subtlety, which allows us to ignore any eagain return results when writing this socket,//and generally cannot do so with nonblocking sockets.

However, if the kernel returns Eagain, you do not need to add//any data to the cache because the main thread has either intended to wake up and read it, or has been awakened and is reading it. If there is a PIPE2 interface, the PIPE2 interface is used to create the #if defined (event__have_pipe2) if (Pipe2 (FD, o_nonblock|
o_cloexec) = = 0) return 0; #endif//If there is no PIPE2, a pipe is used to create #if defined (event__have_pipe) if (pipe (FD) = = 0) {if (Evutil_fast_socke
            T_nonblocking (Fd[0]) < 0 | |
            Evutil_fast_socket_nonblocking (fd[1]) < 0 | |
            Evutil_fast_socket_closeonexec (Fd[0]) < 0 | |
            Evutil_fast_socket_closeonexec (fd[1]) < 0) {close (fd[0]);
            Close (fd[1]);
            Fd[0] = fd[1] =-1;
        return-1;
    } return 0;
    } else {Event_warn ("%s:pipe", __func__); } #endif//ASIf there is neither PIPE2 nor pipe, use socket #ifdef _win32 #define LOCAL_SOCKETPAIR_AF af_inet #else #define LOCAL_SOCKETPAIR_AF Af_unix # endif if (Evutil_socketpair (LOCAL_SOCKETPAIR_AF, sock_stream, 0, fd) = = 0) {if (Evutil_fast_socket_nonblockin
            G (Fd[0]) < 0 | |
            Evutil_fast_socket_nonblocking (fd[1]) < 0 | |
            Evutil_fast_socket_closeonexec (Fd[0]) < 0 | |
            Evutil_fast_socket_closeonexec (fd[1]) < 0) {Evutil_closesocket (fd[0]);
            Evutil_closesocket (fd[1]);
            Fd[0] = fd[1] =-1;
        return-1;
    } return 0;
    } Fd[0] = fd[1] =-1;
return-1; }



Four, event_assign function analysis

/** Prepare A new, already-allocated event structure to be added.  The function event_assign () Prepares the event structure EV to is used in a future calls to Event_add () and Event_del (). Unlike Event_new (), it doesn ' t allocate memory itself:it requires so you have already allocated a struct event, PR  Obably on the heap.  Doing this would typically make your code depend on the size of the event structure, and thereby create incompatibility
  With the future versions of Libevent.
  The easiest-avoid this problem is just to use Event_new () and Event_free () instead. A slightly harder-future-proof your code is-use-event_get_struct_event_size () to determine the required size O
  F an event at runtime.  Note that it is the not safe-to-call the This function on a event that is active or pending.  Doing so would corrupt internal data structures in Libevent, and leads to strange, hard-to-diagnose bugs. You _can_ the use of event_assign to a existing event, but onlyIf it is not active or pending!
  The arguments for this function, and the behavior of the events, it makes, is as for Event_new ().
  @param ev An event struct to is modified @param base the event base to which EV should be attached. @param fd The file descriptor to is monitored @param events desired events to monitor; Can is Ev_read and/or Ev_write @param callback callback function to being invoked when the event occurs @param callback_a
  RG An argument to is passed to the callback function @return 0 if success, or-1 on invalid arguments. @see event_new (), Event_add (), Event_del (), Event_base_once (), event_get_struct_event_size () *//Prepare a new allocated event structure to add
to event_base;//function Event_assign prepare the event structural body EV for future event_add and Event_del calls.
Unlike Event_new, he did not allocate his own memory: he needed you to have allocated an event structure, which should be allocated on the heap;//doing so would generally make your code dependent on the size of the event structure and would result in incompatibility with future versions.
The simplest way to avoid assigning problems is to use event_new and Event_free to apply and release.
A slightly more difficult way to make the code future compatible is to use Event_get_struct_event_size to determine the size of the run-time event. Note that it is not safe to call this function if the event is in an active or pending state. This will destroy L.Ibevent internal data structures,//leads to some strange, hard-to-investigate problems.
You can only use Event_assign to change an existing event, but the status of the event cannot/is active or pending. EV: Event structure to be modified//Base: Event_base//FD to add EV: file descriptor to be monitored//events: Type of event to be monitored; can be ev_read or Ev_write//callback: When an event occurs callback function//CALLBACK_ARG: Arguments passed to the callback function, generally event_base handle//If successful, returns 0, if failure returns-1//Related view Event_new,event_add,event_del,event_ base_once,event_get_struct_event_size int event_assign (struct event *ev, struct event_base *base, evutil_socket_t FD, SH
    ORT events, void (*callback) (evutil_socket_t, short, void *), void *arg) {if (!base) base = Current_base;

    if (arg = = &event_self_cbarg_ptr_) arg = EV;

     Event_debug_assert_not_added_ (EV);

    Event structure initial setup ev->ev_base = base;
    Ev->ev_callback = callback;
    Ev->ev_arg = arg;
    EV-&GT;EV_FD = FD;
    Ev->ev_events = events;
    ev->ev_res = 0;
    Ev->ev_flags = Evlist_init;
    Ev->ev_ncalls = 0;

     Ev->ev_pncalls = NULL; If it is a signal event if (events & ev_signal) {//signal events and IO events cannot exist simultaneously if (Events & (ev_read| ev_write| ev_closed))! = 0) {event_warnx ("%s:ev_signal is not compatible with" "Ev_read, Ev_write or
            Ev_closed ", __func__);
        return-1;
    }//Set the type of callback function to be executed when the event is closed: evcb_callback ev->ev_closure = ev_closure_event_signal; } else {//If it is a different type of event: IO event, timed Event//If the event is a permanent event, that is, after each call does not remove the event List//empty IO timeout control and set the callback function type when the event is closed: Evcb_c
            Allback if (events & Ev_persist) {evutil_timerclear (&ev->ev_io_timeout);
        Ev->ev_closure = ev_closure_event_persist;
        } else {//Set event callback function type: evcb_callback ev->ev_closure = ev_closure_event;

     }}//Event timeout control minimum heap initialization min_heap_elem_init_ (EV);
        By default, the event priority is set to the intermediate priority if (base! = NULL) {/* By default, we put new events into the middle first */
    Ev->ev_pri = base->nactivequeues/2; } event_debug_Note_setup_ (EV);
return 0; }


V. EVSIG_CB internal Signal notifies the pipeline that a callback function is detected when a signal occurs
/* Callback for time the signal handler write a byte to our signaling socket *///When the signal handle is written to the signal processing socket the callback function//ARG is typically EV Ent_base handle//each time the registration signal occurs, the callback function that binds the signal is inserted into the activation queue, static void Evsig_cb (evutil_socket_t fd, short, void *arg) {//used to
    Store signals to inform the pipeline of data, each byte representing a signal static char signals[1024];
    ev_ssize_t N;
     int i;
    NSIG is a Linux system-defined constant//used to store capture signals, each array variable representing a signal int ncaught[nsig];

    struct Event_base *base;

     base = arg;

     Empties the cache memset (&ncaught, 0, sizeof (ncaught)) that stores the capture signal;
     Loops through the signal in the signal pipeline until it is read or unreadable. And will read the signal and record the number of times the signal occurred while (1) {#ifdef _win32 n = recv (fd, signals, sizeof (signals), 0); #else n = r
EAD (FD, signals, sizeof (signals));
            #endif if (n = =-1) {int err = Evutil_socket_geterror (FD); if (!
            Evutil_err_rw_retriable (ERR)) Event_sock_err (1, FD, "%s:recv", __func__);
        Break } else if (n = = 0) {/* XXX warn? */Break;
            }//traversal of each byte in the cache, converted to a signal value//If the signal value is valid, it indicates that the signal occurs once, increasing the number of times the signal occurs for (i = 0; i < n; ++i) {
            ev_uint8_t sig = Signals[i];
        if (Sig < NSIG) ncaught[sig]++;
     }} evbase_acquire_lock (base, Th_base_lock);
    Traversing the current number of signals, activating the event associated with the signal, is actually inserting//signal-related callback functions into the activation event queue.
    for (i = 0; i < NSIG; ++i) {if (Ncaught[i]) evmap_signal_active_ (base, I, ncaught[i]);
} evbase_release_lock (base, Th_base_lock); }



Liu, Evsig_add
When executing a evsigsel->add, this function is actually called. When registering a signal event, the Evsig_add function is called, mainly registering the signal event on the event_base, while binding the signal and the function of the capture signal through the signal system function, and once the function capturing the signal captures the signal, the signal is written to the internal signal notification pipeline. An internal signal notification event is then triggered, and an internal signal notification event reads the signal from the internal signal notification pipeline and then triggers the corresponding external signal event callback function based on the signal value.
Base: Bind to which event_base on//evsignal: Signal to bind//old: Older event type on this signal//events: Current event type on this signal//p: Static int evsig_add not currently used (str UCT event_base *base, evutil_socket_t evsignal, short old, short events, void *p) {//here is actually an internal signal notification event for converting an external signal to an internal event
    Concrete implementation of struct evsig_info *sig = &base->sig;

    (void) P;

    Evutil_assert (evsignal >= 0 && evsignal < NSIG);
    /* Catch Signals if they happen quickly */evsigbase_lock (); if (evsig_base! = Base && evsig_base_n_signals_added) {event_warnx ("added a signal to event base%p with  Signals "" already added to Event_base%p.  Only one can has "" "signals at a time with the%s backend. The base with "the most recently added signal or the most recent" "Event_base_loop () call gets Preference Do "" is rely on the behavior in the future Libevent versions. ", Base, Evsig_base, BASE-&GT;EVSEL-&G
    T;name); }//The following are three global variables, all of which are signal events
     One is the total number of signal events added in the event_base handle//One current Event_base//One is the file descriptor of the signal event, which is actually the base->sig.ev_signal_pair[1 mentioned above],
    That is, the internal pipe used to write the signal evsig_base = base;
    evsig_base_n_signals_added = ++sig->ev_n_signals_added;
    EVSIG_BASE_FD = base->sig.ev_signal_pair[1];

    Evsigbase_unlock ();
     Event_debug (("%s:%d:changing signal Handler", __func__, (int) evsignal)); Register the signal to the signal capture function Evsig_handler//Evsig_handler will capture the signal, then write the signal to base->sig.ev_signal_pair[1], to inform the internal pipeline, and then notify the Event_
    Base processing if (Evsig_set_handler_ (base, (int) evsignal, evsig_handler) = =-1) {goto err;
     }//Time parameter is NULL, indicating that this is not a timeout time, only when registering the event descriptor has read and write behavior, more accurately,//only when the internal signal notifies the pipeline that a write event occurs, the callback function is triggered to read the signal, and then to activate the external signal registration callback function. This is a specific event to add, need to view event registration related Documents///here is actually adding an external signal event when an internal signal notification event is added in order to convert an external signal event with internal pipe//to the external signal to internal IO Event//If EV
     Ent_base has not registered internal notification events, you need to register, if you have already registered, you do not need to re-register. This means that only if an external signal event is registered, the registered internal signal notification event is triggered, otherwise the internal notification event is only applied and not used; if (!sig->ev_signal_added) {if (Event_adD_nolock_ (&sig->ev_signal, NULL, 0)) goto err;
    sig->ev_signal_added = 1;

} return (0);
    Err:evsigbase_lock ();
    --evsig_base_n_signals_added;
    --sig->ev_n_signals_added;
    Evsigbase_unlock ();
Return (-1); }





Seven, Evsig_set_handler_
Set the snap handle for an external signal
/* Helper:set the signal handler for Evsignal to handler on base, so it * we can restore the original handler when we Clear the current one. The signal processing handle is set for the signal in the event_base so that when the current signal is cleared, it is possible to re-store the generic signal processing handle int evsig_set_handler_ (struct event_base *base, int evsignal , void (__cdecl *handler) (int)) {#ifdef event__have_sigaction struct sigaction sa; #else ev_sighandler_t sh; #end
    if struct evsig_info *sig = &base->sig;

    void *p;
     /* * Resize saved signal handler array up to the highest signal number.
     * A dynamic array is used to keep footprint in the low side.
     *///Resize the signal handle array to accommodate the maximum signal value. Dynamic arrays are used to accommodate the current maximum signal value//Once the current signal value is greater than the original maximum signal value, you will need to re-apply the space if (evsignal >= sig->sh_old_max) {int New_max =
        Evsignal + 1; Event_debug (("%s:evsignal (%d) >= Sh_old_max (%d), resizing", __func__, Evsignal, Sig->sh_old_max))
        ;
        p = Mm_realloc (sig->sh_old, New_max * sizeof (*sig->sh_old)); If (p = = NULL)
            {Event_warn ("realloc");
        Return (-1); } memset ((char *) p + sig->sh_old_max * sizeof (*sig->sh_old), 0, (New_max-sig->sh_old_max)

        * sizeof (*sig->sh_old));
        Sig->sh_old_max = New_max;
    Sig->sh_old = p;
    _malloc (sizeof *sig->sh_old[evsignal]);
        if (sig->sh_old[evsignal] = = NULL) {Event_warn ("malloc");
    Return (-1); }/* Save previous handler and setup new handler *//Save the previous signal handle and configure a new signal handle//bind the new signal and signal processing function handler together #ifd
    EF event__have_sigaction memset (&sa, 0, sizeof (SA));
    Sa.sa_handler = handler;
    Sa.sa_flags |= Sa_restart;

    Sigfillset (&sa.sa_mask);
        if (Sigaction (evsignal, &sa, sig->sh_old[evsignal]) = =-1) {Event_warn ("sigaction");
       Mm_free (sig->sh_old[evsignal]); Sig->sh_old[evsignal] = NULL;
    Return (-1);
        } #else if ((SH = signal (evsignal, handler)) = = Sig_err) {Event_warn ("signal");
        Mm_free (sig->sh_old[evsignal]);
        Sig->sh_old[evsignal] = NULL;
    Return (-1);
} *sig->sh_old[evsignal] = sh;
#endif return (0); }



Viii. Evsig_handler This is the processing function when the external signal occurs, mainly used to write the signal value to base->sig.ev_signal_pair[1], in order to achieve the purpose of notifying the external signal.
static void __cdecl
Evsig_handler (int sig)
{
    int save_errno = errno;
#ifdef _win32
    int socket_errno = Evutil_socket_error ();
#endif

    ev_uint8_t msg;

    if (evsig_base = = NULL) {
        event_warnx (
            "%s:received signal%d, but has no base configured",
            __func__, SIG);
        return;
    }

#ifndef event__have_sigaction
    Signal (SIG, Evsig_handler);
#endif/

    * Wake up our notification mechanism *//
    wake-up notification mechanism
     //Use messages written to the internal pipeline to hold the signal value
    msg = sig;
#ifdef _win32
    Send (EVSIG_BASE_FD, (char*) &msg, 1, 0);
#else
    {
          //writes the message to the internal pipe file descriptor, note that it is a byte, because the signal uses a byte to represent the
          //EVSIG_BASE_FD in front of the
        int r = Write ( EVSIG_BASE_FD, (char*) &msg, 1);
        (void) R; /* suppress ' unused return value ' and ' unused var ' */
    }
#endif
    errno = Save_errno;
#ifdef _win32
    evutil_set_socket_error (socket_errno);
#endif
}







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.