libevent源码学习(一)event事件

来源:互联网 发布:linux 禁用显卡 驱动 编辑:程序博客网 时间:2024/05/16 07:16

接触libevent断续有近一个月,准备开始系统学习源码,参考资料《libevent源码深度剖析》张亮

先看看事件结构体

#define TAILQ_ENTRY(type)                       \struct {                                \    struct type *tqe_next;  /* next element */          \    struct type **tqe_prev; /* address of previous next element */  \}struct event {    TAILQ_ENTRY (event) ev_next;    TAILQ_ENTRY (event) ev_active_next;    TAILQ_ENTRY (event) ev_signal_next;    unsigned int min_heap_idx;  /* for managing timeouts */    struct event_base *ev_base;    int ev_fd;    short ev_events;    short ev_ncalls;    short *ev_pncalls;  /* Allows deletes in callback */    struct timeval ev_timeout;    int ev_pri;     /* smaller numbers are higher priority */    void (*ev_callback)(int, short, void *arg);    void *ev_arg;    int ev_res;     /* result passed to event callback */    int ev_flags;};

看一下里面的成员:

1. short ev_events

event关注的事件类型,源码中宏如下:

//超时事件#define EV_TIMEOUT  0x01//io事件#define EV_READ     0x02#define EV_WRITE    0x04//信号#define EV_SIGNAL   0x08//辅助选项#define EV_PERSIST  0x10    /* Persistant event */

libevent通过struct event这一个结构体,将IO事件,信号,超时事件统一管理

2. 事件链表

ev_nextev_active_nextev_signal_next都是双向链表节点指针;它们是libevent对不同事件类型和在不同的时期,对事件管理时使用到的字段

3. 超时事件管理

min_heap_idxev_timeoout,如果是timeout事件,则分别保存该超时事件在小根堆中的索引,和超时值。

4. ev_fd

对于IO事件,它绑定了一个文件描述符,对于signal事件,绑定的信号

5. ev_callback 事件回调

void (*ev_callback)(int fd,short events,void *arg)//fd对应ev_fd events对应ev_events arg对应ev_arg

事件设置的接口函数

通过event_newstruct event分配空间,并进行赋值

struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg){    struct event *ev;    ev = mm_malloc(sizeof(struct event));//分配空间    if (ev == NULL)        return (NULL);    if (event_assign(ev, base, fd, events, cb, arg) < 0) {        mm_free(ev);//如果分配失败,将释放内存        return (NULL);    }    return (ev);}

进而进入,event_assign里面看看赋值:

intevent_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg){//设置event_base    if (!base)        base = current_base;    _event_debug_assert_not_added(ev);    //设置    ev->ev_base = base;    ev->ev_callback = callback;    ev->ev_arg = arg;    ev->ev_fd = fd;    ev->ev_events = events;    ev->ev_res = 0;    ev->ev_flags = EVLIST_INIT;    ev->ev_ncalls = 0;    ev->ev_pncalls = NULL;    if (events & EV_SIGNAL) {//信号事件        if ((events & (EV_READ|EV_WRITE)) != 0) {            event_warnx("%s: EV_SIGNAL is not compatible with "                "EV_READ or EV_WRITE", __func__);            return -1;        }        ev->ev_closure = EV_CLOSURE_SIGNAL;    } else {        if (events & EV_PERSIST) {            evutil_timerclear(&ev->ev_io_timeout);            ev->ev_closure = EV_CLOSURE_PERSIST;        } else {            ev->ev_closure = EV_CLOSURE_NONE;        }    }    //初始化小根堆    min_heap_elem_init(ev);    if (base != NULL) {        /* by default, we put new events into the middle priority */        ev->ev_pri = base->nactivequeues / 2;    }    _event_debug_note_setup(ev);    return 0;}

libevent有一个全局的event_base指针current_base,默认情况下,event将被注册到这个默认的current_base上,如果用户没有特别指定base

设置完成之后,调用event_add来注册这个事件,来看看这个函数

intevent_add(struct event *ev, const struct timeval *tv){    int res;    if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {        event_warnx("%s: event has no event_base set.", __func__);        return -1;    }    EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);    res = event_add_internal(ev, tv, 0);//真正的add操作    EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);    return (res);}

event_add_internal是一个比较复杂的函数:
首先,他会在小根堆中为超时事件预留一个hole

if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {    if (min_heap_reserve(&base->timeheap,1 + min_heap_size(&base->timeheap)) == -1)        return (-1);  /* ENOMEM == errno */    }

然后,针对I/O事件和信号,分别处理

    if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&//IO或者信号        !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {        if (ev->ev_events & (EV_READ|EV_WRITE))            res = evmap_io_add(base, ev->ev_fd, ev);//IO        else if (ev->ev_events & EV_SIGNAL)//信号            res = evmap_signal_add(base, (int)ev->ev_fd, ev);        if (res != -1)            event_queue_insert(base, ev, EVLIST_INSERTED);//插入到队列中        if (res == 1) {            /* evmap says we need to notify the main thread. */            notify = 1;            res = 0;        }    }

最后,如果是超时事件,当事件插入完毕之后,需要将超时时间插入到之前预留的hole中去,代码略:

    if (res != -1 && tv != NULL) {    //...    }
1 0
原创粉丝点击