看这篇之前可以看这篇基础
libevent 学习准备
版本:2.1.11
1.分析例子
来源
void accept_cb(int fd, short events, void* arg);
int tcp_server_init(int port, int listen_num);
int main(int argc, char** argv) {
int listener = tcp_server_init(9999, 10);
if( listener == -1 )
{
perror(" tcp_server_init error ");
return -1;
}
struct event_base* base = event_base_new();
//添加监听客户端请求连接事件
struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,
accept_cb, base);
event_add(ev_listen, NULL);
event_base_dispatch(base);
event_base_free(base);
return 0;
}
void accept_cb(int fd, short events, void* arg) {
...
}
typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num) {
int errno_save;
evutil_socket_t listener;
listener = ::socket(AF_INET, SOCK_STREAM, 0);
if( listener == -1 )
return -1;
//允许多次绑定同一个地址。要用在socket和bind之间
evutil_make_listen_socket_reuseable(listener);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(port);
if( ::bind(listener, (SA*)&sin, sizeof(sin)) < 0 )
goto error;
if( ::listen(listener, listen_num) < 0)
goto error;
//跨平台统一接口,将套接字设置为非阻塞状态
evutil_make_socket_nonblocking(listener);
return listener;
error:
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save;
return -1;
}
这里,我们反向来分析,我们先不管怎么初始化变量,我们先看accept_cb这个回调函数怎么触发的。
先看这个函数 event_base_dispatch 位于event.c
int
event_base_dispatch(struct event_base *event_base)
{
return (event_base_loop(event_base, 0));
}
int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
...
event_queue_make_later_events_active(base); // 这个下次讲
res = evsel->dispatch(base, tv_p); // 重点
timeout_process(base); // 下次说,这个不用管
if (N_ACTIVE_CALLBACKS(base)) {
int n = event_process_active(base); // 重点
if ((flags & EVLOOP_ONCE)
&& N_ACTIVE_CALLBACKS(base) == 0
&& n != 0)
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
...
}
evsel 是结构体如下
/** Structure to define the backend of a given event_base. */
struct eventop {
const char *name;
void *(*init)(struct event_base *);
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
int (*dispatch)(struct event_base *, struct timeval *);
void (*dealloc)(struct event_base *);
int need_reinit;
enum event_method_feature features;
size_t fdinfo_len;
};
evsel 是结构体变量,我们关心的是他的初始化,那么他的初始化在哪里呢?
evsel 是base->evsel; base 是我们这里创建的, struct event_base* base = event_base_new();
所以答案就是在这个event_base_new() 帮我们初始化了,我截取一些代码
struct event_base *
event_base_new(void) {
struct event_base *base = NULL;
struct event_config *cfg = event_config_new();
if (cfg) {
base = event_base_new_with_config(cfg);
event_config_free(cfg);
}
return base;
}
struct event_base *
event_base_new_with_config(const struct event_config *cfg) {
...
for (i = 0; eventops[i] && !base->evbase; i++) {
...
base->evsel = eventops[i];
base->evbase = base->evsel->init(base); // [1]
}
...
}
/* Array of backends in order of preference. */
static const struct eventop *eventops[] = {
#ifdef EVENT__HAVE_EVENT_PORTS
&evportops,
#endif
#ifdef EVENT__HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef EVENT__HAVE_EPOLL
&epollops,
#endif
#ifdef EVENT__HAVE_DEVPOLL
&devpollops,
#endif
#ifdef EVENT__HAVE_POLL
&pollops,
#endif
#ifdef EVENT__HAVE_SELECT
&selectops,
#endif
#ifdef _WIN32
&win32ops,
#endif
NULL
};
eventops 是全局变量,看到这里,大家应该就懂了,具体的实现就是eventops这个数组挨个取,取到了初始化成功,后面就不用了,当然可以自定义,具体这里不说了,这里继续分析事件循环
我们这个系统,libevent 用的是epoll,所以我们看下eopll.c 这个文件,
epoll.c 提供了如下方法,实现了struct eventop
const struct eventop epollops = {
"epoll",
epoll_init,
epoll_nochangelist_add,
epoll_nochangelist_del,
epoll_dispatch,
epoll_dealloc,
1, /* need reinit */
EV_FEATURE_ET|EV_FEATURE_O1|EV_FEATURE_EARLY_CLOSE,
0
};
epoll_init, 这个我们刚刚调用了,是这里 [1] 调用了base->evbase = base->evsel->init(base); // [1]
就在event_base_new_with_config,看仔细点.
我先不说这个,
我关心这个函数,
epoll_dispatch 对应res = evsel->dispatch(base, tv_p); // 重点
终于说回来了,接下来看这个
static int
epoll_dispatch(struct event_base *base, struct timeval *tv)
{
struct epollop *epollop = base->evbase;
struct epoll_event *events = epollop->events;
int i, res;
long timeout = -1;
if (tv != NULL) {
timeout = evutil_tv_to_msec_(tv);
if (timeout < 0 || timeout > MAX_EPOLL_TIMEOUT_MSEC) {
/* Linux kernels can wait forever if the timeout is
* too big; see comment on MAX_EPOLL_TIMEOUT_MSEC. */
timeout = MAX_EPOLL_TIMEOUT_MSEC;
}
}
epoll_apply_changes(base);
event_changelist_remove_all_(&base->changelist, base);
EVBASE_RELEASE_LOCK(base, th_base_lock);
res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
...
for (i = 0; i < res; i++) {
int what = events[i].events;
short ev = 0;
if (what & (EPOLLHUP|EPOLLERR)) {
ev = EV_READ | EV_WRITE;
} else {
if (what & EPOLLIN)
ev |= EV_READ;
if (what & EPOLLOUT)
ev |= EV_WRITE;
if (what & EPOLLRDHUP)
ev |= EV_CLOSED;
}
...
if (!ev)
continue;
evmap_io_active_(base, events[i].data.fd, ev | EV_ET);
}
if (res == epollop->nevents && epollop->nevents < MAX_NEVENT) {
/* We used all of the event space this time. We should
be ready for more events next time. */
int new_nevents = epollop->nevents * 2;
struct epoll_event *new_events;
new_events = mm_realloc(epollop->events,
new_nevents * sizeof(struct epoll_event));
if (new_events) {
epollop->events = new_events;
epollop->nevents = new_nevents;
}
}
return (0);
}
epoll_wait 这个很熟悉了,这就是监听这我们之前创建的listener ,listener有读事件触发就会就会返回了,具体是怎么让epoll_wai 监听的,我们知道调用epoll_ctl就可以,epoll_ctl 就是epoll_nochangelist_add 添加的,这理顺便说下epoll_nochangelist_add,他 调用epoll_apply_one_change 这里面调 epoll_ctl 添加的listener ,具体大家自己可以看看,太多了说下去可能打破我的文章逻辑了,好我们接下去evmap_io_active_(base, events[i].data.fd, ev | EV_ET);
epoll_wait 返回拿到是fd就是events[i].data.fd(fd 就是listener ),这个fd 的回调函数我们怎么拿到呢?struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,accept_cb, base);
这里我们将fd(listener ) 和 回调函数accept_cb 初始化了一个struct event,这个event,就存在base 里面,后面我说的全局base和这个base是一个就是struct event_base* base = event_base_new();
,我们的东西丢这里就行了,具体在base哪里呢?接下来看
void
evmap_io_active_(struct event_base *base, evutil_socket_t fd, short events)
{
struct event_io_map *io = &base->io;
struct evmap_io *ctx;
struct event *ev;
#ifndef EVMAP_USE_HT
if (fd < 0 || fd >= io->nentries)
return;
#endif
GET_IO_SLOT(ctx, io, fd, evmap_io);
/*
(ctx) = (struct evmap_io *)((io)->entries[fd])
*/
if (NULL == ctx)
return;
LIST_FOREACH(ev, &ctx->events, ev_io_next) {
if (ev->ev_events & events)
event_active_nolock_(ev, ev->ev_events & events, 1);
}
}
这里`
GET_IO_SLOT(ctx, io, fd, evmap_io);
/*
(ctx) = (struct evmap_io *)((io)->entries[fd])
*/`
是获取struct evmap_io *ctx;
他存在struct event_io_map *io = &base->io; 里面
event_io_map 和evmap_io 具体的分析,我这里不说了,我推荐的那两个专栏有详细解析,
我们拿到evmap_io 这个结构体还是在贴下
struct evmap_io {
struct event_dlist events;
ev_uint16_t nread;
ev_uint16_t nwrite;
ev_uint16_t nclose;
};
events 是个tailq的链表,我们可以把他当个双链表就行了,具体可以看我开头说的,总之他存着我们之前创建的struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST, accept_cb, base);
注意我们是根据fd,io 获取到的,这里真的值得花大时间了解下,具体怎么添加到base->io;的可以看event_add(ev_listen, NULL); 上面我说过自己看的
接着讲
void
event_active_nolock_(struct event *ev, int res, short ncalls)
{
struct event_base *base;
...
base = ev->ev_base;
...
switch ((ev->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {
default:
case EVLIST_ACTIVE|EVLIST_ACTIVE_LATER:
EVUTIL_ASSERT(0);
break;
case EVLIST_ACTIVE:
/* We get different kinds of events, add them together */
ev->ev_res |= res;
return;
case EVLIST_ACTIVE_LATER:
ev->ev_res |= res;
break;
case 0:
ev->ev_res = res;
break;
}
...
event_callback_activate_nolock_(base, event_to_event_callback(ev));
}
event_active_nolock_,因为参数ev 带了base 所以这个函数没有带base进来了,z在看
event_callback_activate_nolock_这个函数有个参数,event_to_event_callback(ev)
static inline struct event_callback *
event_to_event_callback(struct event *ev) {
return &ev->ev_evcallback;
}
struct event_callback {
TAILQ_ENTRY(event_callback) evcb_active_next;
short evcb_flags;
ev_uint8_t evcb_pri; /* smaller numbers are higher priority */
ev_uint8_t evcb_closure;
/* allows us to adopt for different types of events */
union {
void (*evcb_callback)(evutil_socket_t, short, void *);
void (*evcb_selfcb)(struct event_callback *, void *);
void (*evcb_evfinalize)(struct event *, void *);
void (*evcb_cbfinalize)(struct event_callback *, void *);
} evcb_cb_union;
void *evcb_arg;
};
struct event {
struct event_callback ev_evcallback;
/* for managing timeouts */
union {
TAILQ_ENTRY(event) ev_next_with_common_timeout;
int min_heap_idx;
} ev_timeout_pos;
evutil_socket_t ev_fd;
struct event_base *ev_base;
union {
/* used for io events */
struct {
LIST_ENTRY (event) ev_io_next;
struct timeval ev_timeout;
} ev_io;
/* used by signal events */
struct {
LIST_ENTRY (event) ev_signal_next;
short ev_ncalls;
/* Allows deletes in callback */
short *ev_pncalls;
} ev_signal;
} ev_;
short ev_events;
short ev_res; /* result passed to event callback */
struct timeval ev_timeout;
};
可以看到这里把event强转成了event_callback ,好了我们接下来看
int
event_callback_activate_nolock_(struct event_base *base,
struct event_callback *evcb)
{
int r = 1;
if (evcb->evcb_flags & EVLIST_FINALIZING)
return 0;
switch (evcb->evcb_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)) {
default:
EVUTIL_ASSERT(0);
EVUTIL_FALLTHROUGH;
case EVLIST_ACTIVE_LATER:
event_queue_remove_active_later(base, evcb);
r = 0;
break;
case EVLIST_ACTIVE:
return 0;
case 0:
break;
}
event_queue_insert_active(base, evcb);
if (EVBASE_NEED_NOTIFY(base))
evthread_notify_base(base);
return r;}
static void
event_queue_insert_active(struct event_base *base, struct event_callback *evcb){
EVENT_BASE_ASSERT_LOCKED(base);
if (evcb->evcb_flags & EVLIST_ACTIVE) {
/* Double insertion is possible for active events */
return;
}
INCR_EVENT_COUNT(base, evcb->evcb_flags);
evcb->evcb_flags |= EVLIST_ACTIVE;
base->event_count_active++;
MAX_EVENT_COUNT(base->event_count_active_max, base->event_count_active);
EVUTIL_ASSERT(evcb->evcb_pri < base->nactivequeues);
TAILQ_INSERT_TAIL(&base->activequeues[evcb->evcb_pri],
evcb, evcb_active_next);}
event_callback_activate_nolock_ 这个函数就是把event_callback 插入到base的activequeues链表,然后就结束了。
总结下,epoll_wait 返回后,根据fd 在全局base中找到event,然后转成event_callback 插入到全局base 的activequeues,那什么时候从activequeues 取出来执行函数呢?
接下来看回event_base_dispatch
int
event_base_dispatch(struct event_base *event_base)
{
return (event_base_loop(event_base, 0));
}
int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
...
event_queue_make_later_events_active(base); // 这个下次讲
res = evsel->dispatch(base, tv_p); // 重点
timeout_process(base); // 下次说,这个不用管
if (N_ACTIVE_CALLBACKS(base)) {
int n = event_process_active(base); // 重点
if ((flags & EVLOOP_ONCE)
&& N_ACTIVE_CALLBACKS(base) == 0
&& n != 0)
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
...
}
我们说完了 res = evsel->dispatch(base, tv_p); // 重点
,这个把创建的event(激活的) 转成event_callback 插入到全局base 的activequeues,
下面 event_process_active(base); // 重点
就简单了
static int
event_process_active(struct event_base *base)
{
/* Caller must hold th_base_lock */
struct evcallback_list *activeq = NULL;
int i, c = 0;
...
for (i = 0; i < base->nactivequeues; ++i) {
if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {
base->event_running_priority = i;
activeq = &base->activequeues[i];
if (i < limit_after_prio)
c = event_process_active_single_queue(base, activeq,
INT_MAX, NULL);
else
c = event_process_active_single_queue(base, activeq,
maxcb, endtime);
if (c < 0) {
goto done;
} else if (c > 0)
break; /* Processed a real event; do not
* consider lower-priority events */
/* If we get here, all of the events we processed
* were internal. Continue. */
}
}
done:
base->event_running_priority = -1;
return c;
}
static int
event_process_active_single_queue(struct event_base *base,
struct evcallback_list *activeq,
int max_to_process, const struct timeval *endtime)
{
struct event_callback *evcb;
int count = 0;
...
for (evcb = TAILQ_FIRST(activeq); evcb; evcb = TAILQ_FIRST(activeq)) {
struct event *ev=NULL;
if (evcb->evcb_flags & EVLIST_INIT) {
ev = event_callback_to_event(evcb); // 这里转回来了
if (ev->ev_events & EV_PERSIST || ev->ev_flags & EVLIST_FINALIZING)
event_queue_remove_active(base, evcb);
else
event_del_nolock_(ev, EVENT_DEL_NOBLOCK);
...
} else {
event_queue_remove_active(base, evcb);
event_debug(("event_process_active: event_callback %p, "
"closure %d, call %p",
evcb, evcb->evcb_closure, evcb->evcb_cb_union.evcb_callback));
}
if (!(evcb->evcb_flags & EVLIST_INTERNAL))
++count;
base->current_event = evcb;
...
switch (evcb->evcb_closure) {
case EV_CLOSURE_EVENT_SIGNAL:
EVUTIL_ASSERT(ev != NULL);
event_signal_closure(base, ev);
break;
case EV_CLOSURE_EVENT_PERSIST:
EVUTIL_ASSERT(ev != NULL);
event_persist_closure(base, ev);
break;
case EV_CLOSURE_EVENT: {
void (*evcb_callback)(evutil_socket_t, short, void *);
short res;
EVUTIL_ASSERT(ev != NULL);
evcb_callback = *ev->ev_callback;
res = ev->ev_res;
EVBASE_RELEASE_LOCK(base, th_base_lock);
evcb_callback(ev->ev_fd, res, ev->ev_arg);
}
break;
...
break;
default:
EVUTIL_ASSERT(0);
}
...
}
return count;
}
这里就是取出event,执行event的回调函数,然后我们的accept_cb 就被调用了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。