[libevent源碼分析] event_add
來源:程序員人生 發布時間:2015-03-23 07:53:54 閱讀次數:3278次
event_add 把event往當前event中的ev_base追加,如果需要定時,那末tv不能為空
int
event_add(struct event *ev, const struct timeval *tv)
{
struct event_base *base = ev->ev_base; //event_add 會把event加入到他的ev_base成員里
const struct eventop *evsel = base->evsel; //對應linux的epoll相干函數
void *evbase = base->evbase; //對應linux為 struct epollop
/*
* struct epollop
* {
* struct evepoll *fds; //分配nfiles個evepoll對象
* int nfds; //支持的最大軟限制數的句柄(上面的數組個數)
* struct epoll_event* events; //分配nfiles(軟限制數) epoll_event對象
* int nevents; //nevents保存著上面events個數
* int epfd; //保存著epoll_create的句柄
* }
*/
int res = 0;
event_debug((
"event_add: event: %p, %s%s%scall %p",
ev,
ev->ev_events & EV_READ ? "EV_READ " : " ",
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
tv ? "EV_TIMEOUT " : " ",
ev->ev_callback));
assert(!(ev->ev_flags & ~EVLIST_ALL));
if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
if (min_heap_reserve(&base->timeheap,
1 + min_heap_size(&base->timeheap)) == ⑴)
return (⑴); /* ENOMEM == errno */
}
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
res = evsel->add(evbase, ev); //注冊or修改事件到epoll中去
if (res != ⑴)
event_queue_insert(base, ev, EVLIST_INSERTED); //把ev插入到eventqeue中去
}
/*
* 如果說插入事件成功同時設置了超時時間
*/
if (res != ⑴ && tv != NULL) {
struct timeval now;
/*
*如果已存在了超時時間,那末就刪除這個超時節點
*/
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(base, ev, EVLIST_TIMEOUT);
/* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
* removes it from the active list. */
if ((ev->ev_flags & EVLIST_ACTIVE) &&
(ev->ev_res & EV_TIMEOUT)) {
/* See if we are just active executing this
* event in a loop
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
event_queue_remove(base, ev, EVLIST_ACTIVE);
}
gettime(base, &now);
evutil_timeradd(&now, tv, &ev->ev_timeout);
event_debug((
"event_add: timeout in %ld seconds, call %p",
tv->tv_sec, ev->ev_callback));
event_queue_insert(base, ev, EVLIST_TIMEOUT);//加入到最小超時堆中去
}
return (res);
}
如果新加的事件不為空,當前事件不是timeout時間,就為他在時間最小堆上分配1塊可用空間(如果最小堆有空間,就不會重新分配)
關于最小堆可以連接我的githup,libevent用最小堆來管理定時事件,根節點永久是最小的而且算法時間最小
如果事件是EV_READ、EV_WRITE、EV_SIGNAL,并且事件不是插入or激活狀態,那末就加入事件
以epoll為例
static int
epoll_add(void *arg, struct event *ev)
{
struct epollop *epollop = arg;
struct epoll_event epev = {0, {0}};
struct evepoll *evep;
int fd, op, events;
/*
* 信號的原理
* 1 設置信號處理函數,保存原來的信號處理函數到event的ev_base->sh_old中去
* 2 如果是首次添加信號,那末需要為所有信號追加1個信號事件來源自event->ev_base->ev_signal
* 同時設置event->ev_base->ev_signal->ev_signal_added,表示信號回調事件已設置了
* 3 同時把事件追加到event->ev_base->ev_signal->evsigevents[signno]鏈表中去
*/
if (ev->ev_events & EV_SIGNAL) //如果是加入的信號
return (evsignal_add(ev));
fd = ev->ev_fd;
if (fd >= epollop->nfds) {
/* Extent the file descriptor array as necessary */
//如果說當前的epoll中已滿足不了新加入的句柄
if (epoll_recalc(ev->ev_base, epollop, fd) == ⑴)
return (⑴);
}
evep = &epollop->fds[fd];
op = EPOLL_CTL_ADD;
events = 0;
//如果事件之前是可讀
if (evep->evread != NULL) {
events |= EPOLLIN;
op = EPOLL_CTL_MOD;
}
//如果事件之前是可寫
if (evep->evwrite != NULL) {
events |= EPOLLOUT;
op = EPOLL_CTL_MOD;
}
//新加入事件設置的是可讀事件
if (ev->ev_events & EV_READ)
events |= EPOLLIN;
//新加入事件設置的是可寫事件
if (ev->ev_events & EV_WRITE)
events |= EPOLLOUT;
epev.data.ptr = evep;
epev.events = events;
if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == ⑴)
return (⑴);
/* Update events responsible */
if (ev->ev_events & EV_READ)
evep->evread = ev;
if (ev->ev_events & EV_WRITE)
evep->evwrite = ev;
return (0);
}
信號追加,libevent對信號采取sockpair 創建兩個互連socket,信號激起寫可寫sock,可讀加入到事件循環中去
根據信號ev_base->sig.evsigcaught[signumber]來判斷該信號是不是產生,下面是信號追加函數
int
evsignal_add(struct event *ev)
{
int evsignal;
struct event_base *base = ev->ev_base; //信號event的base
struct evsignal_info *sig = &ev->ev_base->sig; //信號在ev_base的管理結構
if (ev->ev_events & (EV_READ|EV_WRITE)) //信號事件不支持可讀可寫事件
event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
evsignal = EVENT_SIGNAL(ev); //信號的fd就是信號number
assert(evsignal >= 0 && evsignal < NSIG); //信號不能超過NSIG這個數
if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) { //如果說該信號鏈表為空
event_debug(("%s: %p: changing signal handler", __func__, ev));
//設置信號處理函數,同時,保存原來的信號處理函數到ev_base->sh_old中去
if (_evsignal_set_handler(
base, evsignal, evsignal_handler) == ⑴)
return (⑴);
/* catch signals if they happen quickly */
evsignal_base = base;
if (!sig->ev_signal_added) {
if (event_add(&sig->ev_signal, NULL))
return (⑴);
sig->ev_signal_added = 1; //加入epoll_wait中去
}
}
//把ev->ev_signal_next加入到sig->evsigevents[evsignal]的鏈表末端
/* multiple events may listen to the same signal */
TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);
return (0);
}
信號回調函數
/*
* 所有的信號都通用1個信號處理函數
*/
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]++; //引發的信號number++
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); //通過寫sockpair寫數據來觸發epoll_wait返回
errno = save_errno;
}
定時事件采取最小堆,定時值+當前事件最為左后超時時間,加入到最小堆中去,主要在event_base_dispatch中使用
libeven 全部源碼分析和sample都在我的 githup
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈