看這篇之前可以看這篇基礎
libevent 學習準備
接下來就開始吧:
先來個例子
來源 https://blog.csdn.net/luotuo44/article/details/39670221
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 就被調用了。