libevent 之IO事件循环

iahob

看这篇之前可以看这篇基础
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 就被调用了。

阅读 1.2k
0 声望
0 粉丝
0 条评论
0 声望
0 粉丝
文章目录
宣传栏