- 以I/O事件为例
- Libevent源码版本2.1.11
1. 注册事件
应用程序调用event_add函数将其添加到注册事件队列中,并将对应的事件注册到事件多路分发器上。
int
evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev)
{
/* 获得event_base的后端I/O复用机制实例 */
const struct eventop *evsel = base->evsel; //就是操作删除、添加的函数
/* 获得event_base中文件描述符与I/O事件队列的映射表*/
struct event_io_map *io = &base->io;
/* fd参数对应的I/O事件队列 */
struct evmap_io *ctx = NULL;
int nread, nwrite, nclose, retval = 0;
short res = 0, old = 0;
struct event *old_ev;
EVUTIL_ASSERT(fd == ev->ev_fd);
if (fd < 0)
return 0;
#ifndef EVMAP_USE_HT
/*I/O事件队列数组io.entries中,每个文件描述符占用一项,如果fd大于当前数组的大小,则增加数组的大小(扩大后的数组的容量要大于fd)*/
if (fd >= io->nentries) {
if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
return (-1);
}
#endif
/* 下面这个宏根据EVMAP_USE_HT是否被定义而有不同的实现,但目的都是创建ctx,
在映射表io中为fd和ctx添加映射关系*/
GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
evsel->fdinfo_len); //得到文件描述符为fd的ctx
nread = ctx->nread;
nwrite = ctx->nwrite;
nclose = ctx->nclose;
/* 原先fd的情况*/
if (nread)
old |= EV_READ;
if (nwrite)
old |= EV_WRITE;
if (nclose)
old |= EV_CLOSED;
//得到res的情况
if (ev->ev_events & EV_READ) {
if (++nread == 1)
res |= EV_READ;
}
if (ev->ev_events & EV_WRITE) {
if (++nwrite == 1)
res |= EV_WRITE;
}
if (ev->ev_events & EV_CLOSED) {
if (++nclose == 1)
res |= EV_CLOSED;
}
//如果读/写/关文件次数超限,返回错误
if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff || nclose > 0xffff)) {
event_warnx("Too many events reading or writing on fd %d",
(int)fd);
return -1;
}
if (EVENT_DEBUG_MODE_IS_ON() &&
(old_ev = LIST_FIRST(&ctx->events)) &&
(old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
event_warnx("Tried to mix edge-triggered and non-edge-triggered"
" events on fd %d", (int)fd);
return -1;
}
//如果新事件,则调用监听
if (res) {
// 这个extra感觉一点用处也没有
void *extra = ((char*)ctx) + sizeof(struct evmap_io);
/* 往事件多路分发器中注册事件,add是事件多路分发器的接口函数之一,
* 对不同的后端I/O复用机制,这些接口函数有不同的实现
* */
// 正式用epoll往io多路分发器中加入该事件,进行监听
if (evsel->add(base, ev->ev_fd,
old, (ev->ev_events & EV_ET) | res, extra) == -1)
return (-1);
retval = 1;
}
ctx->nread = (ev_uint16_t) nread;
ctx->nwrite = (ev_uint16_t) nwrite;
ctx->nclose = (ev_uint16_t) nclose;
//将ev插到I/