libevent事件(三)---event_add和epoll_add
libevent将定时事件、信号、描述符事件都集成到event中,并且分别维护了相关的链表。
因此,event_add
和event_delete
主要是操作链表,并且调用底层的api。
1. event_add函数
首先是超时事件,timer事件用一个小根堆来表示,这个堆中的最小时间可以用来select/poll/epoll参数的最大值.
另外,添加事件分为两个内容,首先会调用底层的add回调,它有可能是epoll_ctl()
,然后会将该event添加到注册事件的链表中
int
event_add(struct event *ev, const struct timeval *tv)
{
struct event_base *base = ev->ev_base;//event_base
const struct eventop *evsel = base->evsel;//evsel为底层的操作
void *evbase = base->evbase;
int res = 0;
//为timer事件分配空间,min_heap_reserve的操作实际上是realloc
if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
if (min_heap_reserve(&base->timeheap,
1 + min_heap_size(&base->timeheap)) == -1)
return (-1); /* ENOMEM == errno */
}//reserve
//添加事件
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
res = evsel->add(evbase, ev);//底层回调函数
if (res != -1)
event_queue_insert(base, ev, EVLIST_INSERTED);//event插入队列
}
/*
* we should change the timout state only if the previous event
* addition succeeded.
*/
if (res != -1 && tv != NULL) {
//当上一步操作成功时,如果是timer事件将调整timer堆
struct timeval now;
/*
* we already reserved memory above for the case where we
* are not replacing an exisiting timeout.
*/
//表明event已经在timer堆中了,删除旧的
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);//计算时间并插入到timer的小根堆中
evutil_timeradd(&now, tv, &ev->ev_timeout);//宏定义:timeout= now + tv
event_queue_insert(base, ev, EVLIST_TIMEOUT);
}
return (res);
}
epoll_add函数
上文提到,相同的接口会调用特定的回调,以epoll为例,将调用封装的epoll_add
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;
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 */
if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
return (-1);
}
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.fd = fd;
epev.events = events;
if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
return (-1);
/* Update events responsible */
if (ev->ev_events & EV_READ)
evep->evread = ev;
if (ev->ev_events & EV_WRITE)
evep->evwrite = ev;
return (0);
}
首先来看传进来的两个参数,在event_add
中有如下调用evsel->add(evbase,ev)
,这个时候就明白evbase的作用了,在event_base_new
调用中,
有base->evbase=base->evsel->init(base)
,回调函数将最终调用epoll_init
创建一个struct epollop
结构体,并返回指针,为什么要这么做?
本质上来说,还是为了可移植性,对于select也会有struct selectop这样的结构体
再看epoll_add
函数,将封装的struct event
和struct epollop
结构体,转化为epoll认识的struct epoll_event
中
还没有评论,来说两句吧...