1. 程式人生 > >libevent原始碼分析(六)

libevent原始碼分析(六)

libevent-1.4/sample/singnal-test.c
接下來看看 event_base_dispatch(base);
這個函式是整個Reactor的核心,是一個loop.
函式定義:

int event_base_dispatch(struct event_base *event_base)
{
  return (event_base_loop(event_base, 0));
}

event_base_dispatch呼叫event_base_loop:

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;
  if (base->sig.ev_signal_added)
  evsignal_base = base;
 done = 0;
 while (!done) {
  /* Terminate the loop if we have been asked to */
  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);
    }
   }
  }
  timeout_correct(base, &tv);
  tv_p = &tv;
  if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
   timeout_next(base, &tv_p);
  } else {
   /* 
    * if we have active events, we just poll new events
    * without waiting.
    */
   evutil_timerclear(&tv);
  }
  /* If we have no events, we just exit */
  if (!event_haveevents(base)) {
   event_debug(("%s: no events registered.", __func__));
   return (1);
  }
  /* update last old time */
  gettime(base, &base->event_tv);
    /* clear time cache */
  base->tv_cache.tv_sec = 0;
  res = evsel->dispatch(base, evbase, tv_p);
  if (res == -1)
   return (-1);
  gettime(base, &base->tv_cache);
  timeout_process(base);
  if (base->event_count_active) {
   event_process_active(base);
   if (!base->event_count_active && (flags & EVLOOP_ONCE))
    done = 1;
  } else if (flags & EVLOOP_NONBLOCK)
   done = 1;
 }
 /* clear time cache */
 base->tv_cache.tv_sec = 0;
 event_debug(("%s: asked to terminate loop.", __func__));
 return (0);
}

可以看到event_base_loop基本上就是一個while迴圈,不斷的處理訊號事件、定時事件以及監聽事件,從程式流程來看,訊號、定時以及監聽事件分別獨立處理。
通過註釋也可以看到,訊號事件的處理不能在多執行緒中使用,原因是使用了全域性變數event_gotsig、event_sigcb,但其實我們註冊的訊號SIGINT函式不在此處處理,可以去看看event_add函式,也是就上篇文章l ibevent原始碼分析(五)。在上篇中曾說過當訊號被捕獲到時,訊號響應函式向ev_signal_pair[0]寫資料,從而導致ev_signal_pair[1]讀事件就緒,從而喚醒event_base_dispatch中的監聽loop,喚醒部分在程式碼
evsel->dispatch(base, evbase, tv_p)——epoll_dispatch
——epoll_wait(epollop->epfd, events, epollop->nevents, timeout)
epoll_wait系統呼叫就是監聽的事件就緒以後會返回,是否阻塞取決於timeout的值,有定時事件時就超時,沒有就阻塞。
可以看到epoll_wait返回後首先處理的就是訊號,可以是喚醒的被正常註冊的訊號,也可以是被訊號影響而直接返回。
sig.evsignal_caught訊號捕獲標誌在
evsignal_process(base)函式中被清空,計數sig->evsigcaught[SIGINT]也被使用到了,訊號事件被插入到啟用佇列
event_active(ev, EV_SIGNAL, ncalls)——event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);//base->event_count_active++;
返回event_base_loop函式看關於base->event_count_active部分的處理:

if (base->event_count_active) {
   event_process_active(base);
   if (!base->event_count_active && (flags & EVLOOP_ONCE))
    done = 1;
  }

在SIGINT事件插入啟用列表時base->event_count_active++,所以會繼續處理event_process_active(base):

static void
event_process_active(struct event_base *base)
{
	 struct event *ev;
	 struct event_list *activeq = NULL;
 	int i;
	 short ncalls;
	 for (i = 0; i < base->nactivequeues; ++i) {
  	if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
 	  activeq = base->activequeues[i];
  	 break;
	  }
	 }
	 for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
 		 if (ev->ev_events & EV_PERSIST)
  			 event_queue_remove(base, ev, EVLIST_ACTIVE);
		  else
   			event_del(ev);
   			 /* Allows deletes to work */
			 ncalls = ev->ev_ncalls;
  			ev->ev_pncalls = &ncalls;
 			 while (ncalls) {
  			 ncalls--;
  			 ev->ev_ncalls = ncalls;
  			 (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
  			 if (event_gotsig || base->event_break) {
     			 	ev->ev_pncalls = NULL;
  			 	 return;
  			 	}
		 	}
 		 ev->ev_pncalls = NULL;
	 }
}

遍歷啟用佇列,處理相應的事件回撥函式。其中
ncalls就是在訊號函式被處理時刻對應的計數sig->evsigcaught[SIGINT]的值,
ev來源於

for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq))

那麼activeq又是來源於base->activequeues[i],看下函式流程,關於base->activequeues[i]部分:
event_base_dispatch(base);
event_base_loop(base, 0);
evsel->dispatch(base, base->evbase, tv_p)//沒有定時事件,tv_p=NULL
epoll_dispatch(base, base->evbase, tv_p)
evsignal_process(base)
其中,在evsignal_process(base)中:

 for (i = 1; i < NSIG; ++i) {
  	ncalls = sig->evsigcaught[i];
  	if (ncalls == 0)
   	continue;
  	sig->evsigcaught[i] -= ncalls;
 	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->evsigevents[i])來源與
event_add(&signal_int, NULL);//signal-test.c
evsel->add(base->evsel, (&signal_int);
evsignal_add(&signal_int);
TAILQ_INSERT_TAIL(&sig->evsigevents[SIGINT], (&signal_int, ev_signal_next);

event_active——event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);

 case EVLIST_ACTIVE:
  	base->event_count_active++;
  	TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
      	ev,ev_active_next);
  	break;

整個都連起來了,很容易知道 for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq))中的ev直接指向signal_int,那麼以下部分就很明白了:

   (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);

直接替換為
signal_cb((int)ev->ev_fd,ev->ev_res,ev->ev_arg)——signal_cb(SIGINT,EV_SIGNAL|EV_PERSIST, &signal_int)
至此,SIGINT的註冊的訊號事件回撥函式開始執行。