程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> libevent源碼淺析(三):libevent的信號的處理

libevent源碼淺析(三):libevent的信號的處理

編輯:關於C++

在libevent中通過使用socketpair建立一對流管道,也就是全雙工管道,來將信號事件與句柄事件統一起來。

先來看數據結構:

struct evsignal_info {
 struct event ev_signal;  ///<所屬的event
 int ev_signal_pair[2];  ///<創建的流管道
 int ev_signal_added;   ///<信號是否已被加入到event中的標記。
 volatile sig_atomic_t evsignal_caught; ///<事件觸發標記,1表示有信號被觸發 
 struct event_list evsigevents[NSIG];  ///<多個事件有可能注冊到同一個信號,因此這裡每個信號的事件都是一個event_list.
 sig_atomic_t evsigcaught[NSIG];  ///<由於一個信號可能被注冊多次,這裡保存信號被捕捉的次數 
#ifdef HAVE_SIGACTION
 struct sigaction **sh_old;
#else
 ev_sighandler_t **sh_old;
#endif
 int sh_old_max;
};

接下來可以看幾個主要的函數:

evsignal_init函數主要用來初始化一些數據結構。

void
evsignal_init(struct event_base *base)
{
 int i;

 ///創建一對流管道
 if (evutil_socketpair(
   AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1)
 event_err(1, "%s: socketpair", __func__);

    ///設置fd
 FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
 FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
    ///初始化sig數據結構
 base->sig.sh_old = NULL;
 base->sig.sh_old_max = 0;
 base->sig.evsignal_caught = 0;
 memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG);
 /* initialize the queues for all events */
    ///在libevent裡面,所有的事件隊列都用tail queue實現,linux下它使用的是linux自帶的taile queue,具體用法可以去看man手冊。
 for (i = 0; i < NSIG; ++i)
 TAILQ_INIT(&base->sig.evsigevents[i]);

    ///設置非阻塞
    evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);

    ///初始化event結構
 event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1],
 EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
 base->sig.ev_signal.ev_base = base;
 base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
}

evsignal_add方法用來加新的信號事件.

int
evsignal_add(struct event *ev)
{
 int evsignal;
 struct event_base *base = ev->ev_base;
 struct evsignal_info *sig = &ev->ev_base->sig;
///信號事件不能使用讀寫來檢測。
 if (ev->ev_events & (EV_READ|EV_WRITE))
 event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
///得到信號值
 evsignal = EVENT_SIGNAL(ev);
 assert(evsignal >= 0 && evsignal < NSIG);
///如果此信號的事件隊列為空則說明此信號第一次被注冊。因此設置信號處理函數,這裡所有的信號都注冊到相同的處理函數evsignal_handler,接下來我們會來分析這個函數。
 if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {
 event_debug(("%s: %p: changing signal handler", __func__, ev));
 if (_evsignal_set_handler(
    base, evsignal, evsignal_handler) == -1)
  return (-1);

 /* catch signals if they happen quickly */
 evsignal_base = base;
///
 if (!sig->ev_signal_added) {
  if (event_add(&sig->ev_signal, NULL))
  return (-1);
  sig->ev_signal_added = 1;
 }
 }

 /* multiple events may listen to the same signal */
 TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);

 return (0);
}

evsignal_handler就是所有信號的處理函數。

static void
evsignal_handler(int sig)
{
 int save_errno = errno;

 if (evsignal_base == NULL) {
 event_warn(
  "%s: received signal %d, but have no base configured",
  __func__, sig);
 return;
 }

///進入此函數,說明信號已經來臨,因此這裡設置捕捉次數,以及此信號已經被捕捉的標記。
 evsignal_base->sig.evsigcaught[sig]++;
 evsignal_base->sig.evsignal_caught = 1;

#ifndef HAVE_SIGACTION
 signal(sig, evsignal_handler);
#endif

///流管道開始發送數據,說明信號已經來臨。此時另一端就會檢測到事件從而調用我們初始化注冊的回調函數。
 /* Wake up our notification mechanism */
 send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
 errno = save_errno;
}

evsignal_process主要是用來將相應的信號事件加入到激活列表中,以便於調用相應的回調函數。

void
evsignal_process(struct event_base *base)
{
 struct evsignal_info *sig = &base->sig;
 struct event *ev, *next_ev;
 sig_atomic_t ncalls;
 int i;

 base->sig.evsignal_caught = 0;
 for (i = 1; i < NSIG; ++i) {
///得到此信號的所有事件數。
 ncalls = sig->evsigcaught[i];
 if (ncalls == 0)
  continue;
///循環遍歷,得到已發生的信號事件。
 for (ev = TAILQ_FIRST(&sig->evsigevents[i]);
   ev != NULL; ev = next_ev) {
  next_ev = TAILQ_NEXT(ev, ev_signal_next);
  if (!(ev->ev_events & EV_PERSIST))
  event_del(ev);
  event_active(ev, EV_SIGNAL, ncalls);
 }

 sig->evsigcaught[i] = 0;
 }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved