/*
*   我们先来看一下事件的创建
*/
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);
    //调用assign 给ev 赋值
if (event_assign(ev, base, fd, events, cb, arg) < ) {
mm_free(ev);
return (NULL);
} return (ev);
} int
event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
{
if (!base)
base = current_base;
if (arg == &event_self_cbarg_ptr_)
arg = ev; event_debug_assert_not_added_(ev);
    //设置ev结构体的各个参数
ev->ev_base = base; ev->ev_callback = callback;
ev->ev_arg = arg;
ev->ev_fd = fd;
ev->ev_events = events;
ev->ev_res = ;
ev->ev_flags = EVLIST_INIT;
ev->ev_ncalls = ;
ev->ev_pncalls = NULL; if (events & EV_SIGNAL) {
        //信号事件和io事件冲突
if ((events & (EV_READ|EV_WRITE|EV_CLOSED)) != ) {
event_warnx("%s: EV_SIGNAL is not compatible with "
"EV_READ, EV_WRITE or EV_CLOSED", __func__);
return -;
}
ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL;
} else {
if (events & EV_PERSIST) {
            //如果事件是persist 清理记0
evutil_timerclear(&ev->ev_io_timeout);
ev->ev_closure = EV_CLOSURE_EVENT_PERSIST;
} else {
ev->ev_closure = EV_CLOSURE_EVENT;
}
} min_heap_elem_init_(ev);// 初始化最小堆索引为 -1 
  //void min_heap_elem_init_(struct event* e) { e->ev_timeout_pos.min_heap_idx = -1; } 
if (base != NULL) {
/* by default, we put new events into the middle priority */
ev->ev_pri = base->nactivequeues / ;
} event_debug_note_setup_(ev); return ;
}
 
libevent事件管理结构
 IO 事件管理
 反应堆中event_base 中
  {
    ........
    struct event_io_map io;  
    struct event_signal_map sigmap;
    ........
  }
                                           ________________
  event_base->io       event_io_map ------> |    evmap_io   |--->|   io  |----->|   io  |---||------  event_dlist events.
                          ————————————————            此文件描述符关心的所有事件链表
                          |               |
                          ————————————————
                          |               |
                             -----------------
         
            struct evmap_io {
            struct event_dlist events; //链表管理一个文件描述符所有关心的io事件
            ev_uint16_t nread;
            ev_uint16_t nwrite;
            ev_uint16_t nclose;
      };
     
          struct evmap_signal {
        struct event_dlist events;//链表管理所有的信号事件
      };       Linux 下的事件的hashmap io与signal相同。
      #define event_io_map event_signal_map
      struct event_signal_map {
          /* An array of evmap_io * or of evmap_signal *; empty entries are
           * set to NULL. */
          void **entries;     //entries 管理的是evmap_io \ evmap_signal
          /* The number of entries available in entries */
          int nentries;
      };
下面是添加一个io事件的过程:
    int evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev)
    {
      const struct eventop *evsel = base->evsel;
      struct event_io_map *io = &base->io;
      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;
    // Linux  use it
    #ifndef EVMAP_USE_HT
      if (fd >= io->nentries) {
            //分配空间
          if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
            return (-1);
      }
    #endif
      //初始化一个evmap_io 并将ctx指针 指向这个结构体 linux下文件描述符即为key。
      GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
                         evsel->fdinfo_len);
      /*
            #define GET_SIGNAL_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)    \
        do {                                \
            if ((map)->entries[slot] == NULL) {            \
                (map)->entries[slot] =                \
                    mm_calloc(1,sizeof(struct type)+fdinfo_len); \
                if (EVUTIL_UNLIKELY((map)->entries[slot] == NULL)) \
                    return (-1);                \
                (ctor)((struct type *)(map)->entries[slot]);    \
            }                            \
            (x) = (struct type *)((map)->entries[slot]);        \
        } while (0)
      */
      //一个event 可能对应多个事件
    // 这里就是上一个的事件类型
      nread = ctx->nread;
      nwrite = ctx->nwrite;
      nclose = ctx->nclose;       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;
      }
    //一个描述符添加的事件个数不能超过oxffff.
    if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff || nclose > 0xffff)) {
        event_warnx("Too many events reading or writing on fd %d",
            (int)fd);
        return -1;
    }     ctx->nread = (ev_uint16_t) nread;
    ctx->nwrite = (ev_uint16_t) nwrite;
    ctx->nclose = (ev_uint16_t) nclose;
    //将新事件加入链表中
    LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);
    return (retval);
}

定时则是由min_heap最小堆的数据结构管理,每次取时间最短的事件。 添加事件: int
event_add_nolock_(struct event *ev, const struct timeval *tv,
int tv_is_absolute)
{
struct event_base *base = ev->ev_base;
int res = ;
int notify = ; EVENT_BASE_ASSERT_LOCKED(base);
event_debug_assert_is_setup_(ev); event_debug((
"event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%s%scall %p",
ev,
EV_SOCK_ARG(ev->ev_fd),
ev->ev_events & EV_READ ? "EV_READ " : " ",
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ",
tv ? "EV_TIMEOUT " : " ",
ev->ev_callback)); EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));
    //若事件标志是终止事件, 就不再将事件加入
if (ev->ev_flags & EVLIST_FINALIZING) {
/* XXXX debug */
return (-);
} /*
* prepare for timeout insertion further below, if we get a
* failure on any step, we should not change any state.
*/
//先把堆空间准备好, 后续再插入定时事件
if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
if (min_heap_reserve_(&base->timeheap,
+ min_heap_size_(&base->timeheap)) == -)
return (-); /* ENOMEM == errno */
} /* If the main thread is currently executing a signal event's
* callback, and we are not the main thread, then we want to wait
* until the callback is done before we mess with the event, or else
* we can race on ev_ncalls and ev_pncalls below. */
#ifndef EVENT__DISABLE_THREAD_SUPPORT
if (base->current_event == event_to_event_callback(ev) &&
(ev->ev_events & EV_SIGNAL)
&& !EVBASE_IN_THREAD(base)) {
++base->current_event_waiters;
EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);
}
#endif if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {
        //如果事件是未被加入的 那么就先加入map中  上面所述
if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
res = evmap_io_add_(base, ev->ev_fd, ev);
else if (ev->ev_events & EV_SIGNAL)
res = evmap_signal_add_(base, (int)ev->ev_fd, ev);
if (res != -)
event_queue_insert_inserted(base, ev); //此处并没有将event加入到queue 只是将事件中的flag指为EVLIST_INSERTED  (base)->event_count += 1
if (res == ) {
/* evmap says we need to notify the main thread. */
notify = ;
res = ;
}
} /*
* we should change the timeout state only if the previous event
* addition succeeded.
*/
    //添加定时器事件
if (res != - && tv != NULL) {
struct timeval now;
int common_timeout;
#ifdef USE_REINSERT_TIMEOUT
int was_common;
int old_timeout_idx;
#endif /*
* for persistent timeout events, we remember the
* timeout value and re-add the event.
*
* If tv_is_absolute, this was already set.
*/
if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute)
ev->ev_io_timeout = *tv; #ifndef USE_REINSERT_TIMEOUT
if (ev->ev_flags & EVLIST_TIMEOUT) {
event_queue_remove_timeout(base, ev); //已有定时事件将从最小堆中删除原先那个定时事件
}
#endif /* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
* removes it from the active list. */          //如果此事件已经被激活并且返回了timeout事件就将事件终止, 并删除被激活的定时器事件 进而重新调度定时器
if ((ev->ev_flags & EVLIST_ACTIVE) &&
(ev->ev_res & EV_TIMEOUT)) {
if (ev->ev_events & EV_SIGNAL) {
/* See if we are just active executing
* this event in a loop
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = ;
}
} event_queue_remove_active(base, event_to_event_callback(ev));
}
        //初始化定时器
gettime(base, &now);
        //选用链表记时器还是最小堆
common_timeout = is_common_timeout(tv, base);
#ifdef USE_REINSERT_TIMEOUT
was_common = is_common_timeout(&ev->ev_timeout, base);
old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);
#endif if (tv_is_absolute) {
ev->ev_timeout = *tv;
} else if (common_timeout) {
struct timeval tmp = *tv;
tmp.tv_usec &= MICROSECONDS_MASK;
evutil_timeradd(&now, &tmp, &ev->ev_timeout);
ev->ev_timeout.tv_usec |=
(tv->tv_usec & ~MICROSECONDS_MASK);
} else {
evutil_timeradd(&now, tv, &ev->ev_timeout);
} event_debug((
"event_add: event %p, timeout in %d seconds %d useconds, call %p",
ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback)); #ifdef USE_REINSERT_TIMEOUT
event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);
#else
event_queue_insert_timeout(base, ev);//加入到最小堆
#endif
        //多线程定时器
if (common_timeout) {
            //链表形式的计时器
struct common_timeout_list *ctl =
get_common_timeout_list(base, &ev->ev_timeout);
if (ev == TAILQ_FIRST(&ctl->events)) {
common_timeout_schedule(ctl, &now, ev);
}
} else {
            //最小堆计时器
struct event* top = NULL;
/* See if the earliest timeout is now earlier than it
* was before: if so, we will need to tell the main
* thread to wake up earlier than it would otherwise.
* We double check the timeout of the top element to
* handle time distortions due to system suspension.
*/
if (min_heap_elt_is_top_(ev))
notify = ;
else if ((top = min_heap_top_(&base->timeheap)) != NULL &&
evutil_timercmp(&top->ev_timeout, &now, <))
notify = ;
}
} /* if we are not in the right thread, we need to wake up the loop */
   //若为多线程 则需要唤醒主线程进行防止主线程仍阻塞在select/poll/epoll_wait. pipe/eventfd
if (res != - && notify && EVBASE_NEED_NOTIFY(base))
evthread_notify_base(base); event_debug_note_add_(ev); return (res);
}

Libevent 事件管理和添加事件的更多相关文章

  1. DOM2级事件对象、添加事件、阻止默认事件、阻止冒泡事件、获取事件对象目标的兼容处理

    事件对象——兼容处理 /* * 功能: 事件对象兼容 * 参数: 表示常规浏览器的事件对象e */ function getEvent(e) { // 如果存在e存在,直接返回,否则返回window. ...

  2. Nginx事件管理之定时器事件

    1. 缓存时间 1.1 管理 Nginx 中的每个进程都会单独地管理当前时间.ngx_time_t 结构体是缓存时间变量的类型: typedef struct { /* 格林威治时间1970年1月1日 ...

  3. mui 事件管理及自定义事件详解

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  4. 添加事件及Event对象的兼容写法

    一.事件流 事件流描述的是从页面中接受事件的顺序. IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流 1.事件冒泡 事件冒泡,即事件最开始由最具体的元素(文档中嵌套层次最深的那个节点) ...

  5. 整理之DOM事件阶段、冒泡与捕获、事件委托、ie事件和dom模型事件、鼠标事件

    整理之DOM事件阶段 本文主要解决的问题: 事件流 DOM事件流的三个阶段 先理解流的概念 在现今的JavaScript中随处可见.比如说React中的单向数据流,Node中的流,又或是今天本文所讲的 ...

  6. IE和DOM事件流、普通事件和绑定事件的区别

    IE和DOM事件流的区别 IE采用冒泡型事件 Netscape(网络信息浏览器)使用捕获型事件 DOM使用先捕获后冒泡型事件 示例: <body> <div> <butt ...

  7. js事件常用操作、事件流

    注册事件 给元素添加事件,称为注册事件或者绑定事件. 注册事件有两种方式:传统方式和方法监听注册方式 传统方式 on开头的事件,例如onclick <button onclick="a ...

  8. ASP.NET实现微信功能(1)(创建菜单,验证,给菜单添加事件)

    LZ实在 不知道怎么起名字了,索性就取了这个名字,开始吧,说实在的,想给自己的平常的学习做一个总结,总是忘了总结.也只能给工作做一个总结了. 我打算用2篇文章来写,第一篇是关于订阅号的,就是这个号,另 ...

  9. jquery技巧之让任何组件都支持类似DOM的事件管理

    本文介绍一个jquery的小技巧,能让任意组件对象都能支持类似DOM的事件管理,也就是说除了派发事件,添加或删除事件监听器,还能支持事件冒泡,阻止事件默认行为等等.在jquery的帮助下,使用这个方法 ...

随机推荐

  1. LINUX 笔记-特定shell变量

    $# 传递到脚本的参数个数 $* 以一个单字符串显示所有向脚本传递的参数.与位置变量不同,此选项参数可超过9个 $$ 脚本运行的当前进程ID号 $! 后台运行的最后一个进程的进程ID号 $@ 与$*相 ...

  2. h5样式初始化

    nav, header, section, article, aside, footer { display: block; } body, p, pre, hr, ul, dl, dd, h1, h ...

  3. common lisp和scheme的区别

    1. 在Common Lisp 眼中,一个符号的symbol-value 和symbol-function 是不一样的,而Scheme对两者不作区分.在Scheme 里面,变量只有唯一对应的值,它可以 ...

  4. 关于在Python下安装布隆过滤器(bloomfilter)的方法

    由于在爬虫代码中需要实现信息的去重功能,所以需借助bloomfilter,在看完各种博客后发现没有安装,这就尴尬了,不会连门都找不到吧.那就安装呗,各种错误,查看官方文档:http://axiak.g ...

  5. Problem W

    Problem Description Speakless很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了.要申请国外的任何大学,你都要交纳一定的申请费用 ...

  6. 关于php加密库加密数据上传数据库或解密出错的问题

    php加密拓展库随着php版本的更新,函数的使用方法有所改变,所以加密模式推荐使用ecb,其中加密算法19种,加密模式8种,通过这种方式加密后的数据上传数据库后提取出来进行解密会发现结果是乱码,我认为 ...

  7. JavaScript 和 TypeScript 交叉口 —— 类型定义文件(*.d.ts)

    在 <从 JavaScript 到 TypeScript 系列> 文章我们已经学习了 TypeScript 相关的知识. TypeScript 的核心在于静态类型,我们在编写 TS 的时候 ...

  8. C# orderby子句

    注意:对联接运算的结果进行排序. 请注意,排序在联接之后执行. 虽然可以在联接之前将 orderby 子句用于一个或多个源序列,不过通常不建议这样做. 某些 LINQ 提供程序可能不会在联接之后保留该 ...

  9. Node.js 常用工具

    Node.js 常用工具 util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足. util.inherits util.inherit ...

  10. phonegap与H5中的接口对比

    接口 HTML5 phonegap 差异 地理定位 geolocation 单次定位: navigator.geolocation.getCurrentPosition(Success, [error ...