1. event-internal.h中定义了eventop结构体,每一种IO多路复用机制都会实现eventop结构体中的五个方法。
struct eventop {
    const char* name;
    void* (*init)(struct event_base*);
    int (*add)(void*, struct event*);
    int (*del)(void*, struct event*);
    int (*dispatch)(struct event_base*, void*, struct timeval*);
    void (*dealloc)(struct event_base*, void*);
    /* set if we need to reinitialize the event base */
    int need_reinit;
};
  1. select.c、epoll.c、poll.c中分别实现了eventop结构体中的五个方法,对应了select、epoll、poll这三种IO多路复用机制。以epoll模型为例,其实现如下所示:
const struct eventop epollops = {
    "epoll",
    epoll_init,
    epoll_add,
    epoll_del,
    epoll_dispatch,
    epoll_dealloc,
    1 /* need reinit */
};
// epoll_init/epoll_add等是具体的函数实现
  1. 注意到以上函数中中的void* arg参数,这是一个IO多路复用模型上下文,通过epoll_init函数初始化得到。其定义如下:
// IO多路复用模型上下文
struct epollop {
    // fds指向一个struct evepoll数组
    // 对应的event管理,通过fds[sock_fd]得到与socket关联的evepoll
    // 
    struct evepoll* fds;
    // fds的数量
    int nfds;

    // epoll相关
    struct epoll_event* events;
    // struct epoll_event结构体的大小
    int nevents;
    int epfd /*epoll_create(32000)*/;
};
  1. 各个函数的功能主要是:
    1. epoll_init:这个函数会在初始化事件处理框架event_base时,根据不同的操作系统选择IO多路复用机制时使用。函数核心功能是使用epoll_create创建epoll实例、初始化表示IO多路复用模型上下文的结构体epollop
    static void*
    epoll_init(struct event_base* base)
    {
        int epfd;
        struct epollop* epollop;
    
        /* Disable epollueue when this environment variable is set */
        if (evutil_getenv("EVENT_NOEPOLL"))
            return (NULL);
    
        /* Initalize the kernel queue */
        if ((epfd = epoll_create(32000)) == -1) {
            if (errno != ENOSYS)
                event_warn("epoll_create");
            return (NULL);
        }
    
        FD_CLOSEONEXEC(epfd);
    
        if (!(epollop = calloc(1, sizeof(struct epollop))))
            return (NULL);
    
        epollop->epfd = epfd;
    
        /* Initalize fields */
        epollop->events = malloc(INITIAL_NEVENTS * sizeof(struct epoll_event));
        if (epollop->events == NULL) {
            free(epollop);
            return (NULL);
        }
        epollop->nevents = INITIAL_NEVENTS;
    
        epollop->fds = calloc(INITIAL_NFILES, sizeof(struct evepoll));
        if (epollop->fds == NULL) {
            free(epollop->events);
            free(epollop);
            return (NULL);
        }
        epollop->nfds = INITIAL_NFILES;
    
        // 使用socketpair函数创建一对套接字ev_signal_pair[0]和ev_signal_pair[1]
        // 初始化ev_signal事件:监听ev_signal_pair[1]上的读事件,并设置读事件发生的回调
        evsignal_init(base);
    
        return (epollop);
    }
    
    1. epoll_add:重新初始化event_base时使用。主要功能是对一些变量的重新赋值
    2. epoll_dispatch:主要功能是调用epoll_wait检测epoll树中是否有就绪的文件描述符,如果有则将就绪文件描述符上的事件加入激活队列
    static int
    epoll_dispatch(struct event_base* base, void* arg, struct timeval* tv)
    {
        struct epollop* epollop = arg;
        struct epoll_event* events = epollop->events;
        struct evepoll* evep;
        int i, res, timeout = -1;
        // 得到毫秒msecond
        if (tv != NULL)
            timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
    
        if (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;
        }
    
        res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
    
        if (res == -1) {
            if (errno != EINTR) {
                event_warn("epoll_wait");
                return (-1);
            }
            // 产生中断,处理信号
            evsignal_process(base);
            return (0);
        } else if (base->sig.evsignal_caught) {
            // 捕捉到信号
            evsignal_process(base);
        }
    
        event_debug(("%s: epoll_wait reports %d", __func__, res));
        // 处理socket读写
        for (i = 0; i < res; i++) {
            int what = events[i].events;
            struct event* evread = NULL, *evwrite = NULL;
            int fd = events[i].data.fd;
    
            // 非法fd
            if (fd < 0 || fd >= epollop->nfds)
                continue;
            evep = &epollop->fds[fd];
    
            if (what & (EPOLLHUP | EPOLLERR)) {
                evread = evep->evread;
                evwrite = evep->evwrite;
            } else {
                if (what & EPOLLIN) {
                    evread = evep->evread;
                }
    
                if (what & EPOLLOUT) {
                    evwrite = evep->evwrite;
                }
            }
    
            if (!(evread || evwrite))
                continue;
    
            if (evread != NULL)
                // 将事件放入激活队列
                event_active(evread, EV_READ, 1);
            if (evwrite != NULL)
                event_active(evwrite, EV_WRITE, 1);
        }
    
        // 扩容
        if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
            /* 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 = realloc(epollop->events,
                                 new_nevents * sizeof(struct epoll_event));
            if (new_events) {
                epollop->events = new_events;
                epollop->nevents = new_nevents;
            }
        }
    
        return (0);
    }
    
    1. epoll_del:对于信号事件,调用evsignal_del删除;对于IO读写事件则调用epoll_ctl取消检测指定fd的读写事件
    2. epoll_dealloc:资源释放。该函数会在调用event_base_free时使用
  2. 事件处理框架event_base中存在一个成员const struct eventop* evsel;表示Libevent选择的IO多路复用机制。在使用event_base_new函数对event_base进行初始化时,会选择一个具体的IO复用机制。
#ifdef HAVE_EVENT_PORTS
extern const struct eventop evportops;
#endif
#ifdef HAVE_SELECT
extern const struct eventop selectops;
#endif
#ifdef HAVE_POLL
extern const struct eventop pollops;
#endif
#ifdef HAVE_EPOLL
extern const struct eventop epollops;
#endif
#ifdef HAVE_WORKING_KQUEUE
extern const struct eventop kqops;
#endif
#ifdef HAVE_DEVPOLL
extern const struct eventop devpollops;
#endif
#ifdef WIN32
extern const struct eventop win32ops;
#endif

// 以上的struct eventop成员都在select.c、epoll.c、poll.c等文件中定义

/* In order of preference */
static const struct eventop* eventops[] = {
#ifdef HAVE_EVENT_PORTS
    & evportops,
#endif
#ifdef HAVE_WORKING_KQUEUE
    & kqops,
#endif
#ifdef HAVE_EPOLL
    & epollops,
#endif
#ifdef HAVE_DEVPOLL
    & devpollops,
#endif
#ifdef HAVE_POLL
    & pollops,
#endif
#ifdef HAVE_SELECT
    & selectops,
#endif
#ifdef WIN32
    & win32ops,
#endif
    NULL
};

// event_base_new方法内部
base->evbase = NULL;
for (i = 0; eventops[i] && !base->evbase; i++) {
    base->evsel = eventops[i];
    // 初始化IO多路复用模型上下文
    base->evbase = base->evsel->init(base);
}

综上:Libevent通过一个const struct eventop* evsel;指针实现对不同平台的IO多路复用机制提供统一的支持方式。不同平台会实现eventop结构体中的函数指针,而Libevent选择何种IO多路复用机制在编译期间决定。