libevent6

来源:互联网 发布:mysql创建用户sql语句 编辑:程序博客网 时间:2024/06/06 06:50

evmap_io_add与evmap_signal_add分析()

io事件中用到了event hash //参考http://blog.csdn.net/luotuo44/article/details/38403241

哈希结构体分析

TAILQ_HEAD (event_list, event);
#define TAILQ_HEAD(name, type)\struct name {\struct type *tqh_first;/* first element */\struct type **tqh_last;/* addr of last next element */\}
可知: 

struct event_list{

struct event *tqh_first;

struct event  **tqh_last;

}

struct evmap_io {struct event_dlist events;ev_uint16_t nread;ev_uint16_t nwrite;ev_uint16_t nclose;};LIST_HEAD (event_dlist, event); #define LIST_HEAD(name, type)\struct name { \struct type *lh_first;/* first element */ \}
即:

struct evmap_io {
struct event_dlist { struct event * lh_fiest ;}  events;
ev_uint16_t nread;
ev_uint16_t nwrite;
ev_uint16_t nclose;
};

struct event_map_entry {HT_ENTRY(event_map_entry) map_node;evutil_socket_t fd;union { /* This is a union in case we need to make more things that can   be in the hashtable. */struct evmap_io evmap_io;} ent;};

//ht-internal.h文件
#ifdef HT_CACHE_HASH_VALUES
#define HT_ENTRY(type)                          \
  struct {                                      \
    struct type *hte_next;                      \
    unsigned hte_hash;                          \
  }
#else
#define HT_ENTRY(type)                          \
  struct {                                      \
    struct type *hte_next;                      \
  }
#endif

其中::变量hte_hash就是用来存储后面的hashsocket的返回值

当第一次计算得到后,就存放到hte_hash变量中。以后需要用到(会经常用到),就直接向这个变量要即可,无需再次计算hashsocket函数。如果没有这个变量,那么需要用到这个值,都要调用hashsocket函数计算一次。这一点从后面的代码可以看到。


event_io_map分析:

HT_HEAD(event_io_map, event_map_entry);

即:

  struct event_io_map{                                                          //哈希表      struct event_map_entry **hth_table;      //哈希表的长度      unsigned hth_table_length;      //哈希的元素个数      unsigned hth_n_entries;      //resize 之前可以存多少个元素      //在event_io_map_HT_GROW函数中可以看到其值为hth_table_length的      //一半。但hth_n_entries>=hth_load_limit时,就会发生增长哈希表的长度      unsigned hth_load_limit;      //后面素数表中的下标值。主要是指明用到了哪个素数      int hth_prime_idx;    }



结构体event_io_map指明了哈希表的存储位置、哈希表的长度、元素个数等数据。该哈希表是使用链地址法解决冲突问题的这一点可以从hth_talbe成员变量看到。它是一个二级指针,因为哈希表的元素是event_map_entry指针。
 除了怎么解决哈希冲突外,哈希表还有一个问题要解决,那就是哈希函数。这里的哈希函数就是模(%)。用event_map_entry结构体中的fd成员值模 event_io_map结构体中的hth_table_length。

两个发生了冲突的哈希表元素event_map_entry用一个next指向连在一起了(链地址解决冲突)。

从图或者从前面关于event_map_entry结构体的定义可以看到,它有一个evmap_io结构体。而这个evmap_io结构体又有一个struct event_list 类型的成员,而struct event_list类型就是一个TAILQ_HEAD

哈希函数

static inline unsigned
hashsocket(struct event_map_entry *e)
{
/* On win32, in practice, the low 2-3 bits of a SOCKET seem not to
* matter.  Our hashtable implementation really likes low-order bits,
* though, so let's do the rotate-and-add trick. */
unsigned h = (unsigned) e->fd;
h += (h >> 2) | (h << 30);
return h;
}

event_io_map_HT_INIT函数分析:

 static inline void                                                    \  name##_HT_INIT(struct name *head) {                                   \    head->hth_table_length = 0;                                         \    head->hth_table = NULL;                                             \    head->hth_n_entries = 0;                                            \    head->hth_load_limit = 0;                                           \    head->hth_prime_idx = -1;                                           \  }         
static inline void event_io_map_HT_INIT(struct event_io_map *head)  {      head->hth_table_length = 0;      head->hth_table = NULL;      head->hth_n_entries = 0;      head->hth_load_limit = 0;      head->hth_prime_idx = -1;  }  

//该函数返回的是查找节点的前驱节点的hte_next成员变量的地址。//所以返回值肯定不会为NULL,但是对返回值取*就可能为NULLstatic inline struct event_map_entry **_event_io_map_HT_FIND_P(struct event_io_map *head,                        struct event_map_entry *elm){    struct event_map_entry **p;    if (!head->hth_table)        return NULL;#ifdef HT_CACHE_HASH_VALUES    p = &((head)->hth_table[((elm)->map_node.hte_hash)            % head->hth_table_length]);#else    p = &((head)->hth_table[(hashsocket(*elm))%head->hth_table_length]);#endif    //这里的哈希表是用链地址法解决哈希冲突的。    //上面的 % 只是找到了冲突链的头。现在是在冲突链中查找。    while (*p)    {        //判断是否相等。在实现上,只是简单地根据fd来判断是否相等        if (eqsocket(*p, elm))            return p;        //p存放的是hte_next成员变量的地址        p = &(*p)->map_node.hte_next;    }    return p;}
//在event_io_map这个哈希表中,找个表项elmstatic inline struct event_map_entry *event_io_map_HT_FIND(const struct event_io_map *head,                     struct event_map_entry *elm){    struct event_map_entry **p;    struct event_io_map *h = (struct event_io_map *) head;#ifdef HT_CACHE_HASH_VALUES    do    {   //计算哈希值        (elm)->map_node.hte_hash = hashsocket(elm);    } while(0);#endif    p = _event_io_map_HT_FIND_P(h, elm);    return p ? *p : NULL;}

/* Insert the element 'elm' into the table 'head'.  Do not call this * function if the table might already contain a matching element. */static inline voidevent_io_map_HT_INSERT(struct event_io_map *head,                       struct event_map_entry *elm){    struct event_map_entry **p;    if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit)        event_io_map_HT_GROW(head, head->hth_n_entries+1);    ++head->hth_n_entries;#ifdef HT_CACHE_HASH_VALUES    do    {   //计算哈希值.此哈希不同于用%计算的简单哈希。        //存放到hte_hash变量中,作为cache        (elm)->map_node.hte_hash = hashsocket(elm);    } while (0);    p = &((head)->hth_table[((elm)->map_node.hte_hash)            % head->hth_table_length]);#else    p = &((head)->hth_table[(hashsocket(*elm))%head->hth_table_length]);#endif    //使用头插法,即后面才插入的链表,反而会在链表头。    elm->map_node.hte_next = *p;    *p = elm;}

/* Insert the element 'elm' into the table 'head'. If there already * a matching element in the table, replace that element and return * it. */static inline struct event_map_entry *event_io_map_HT_REPLACE(struct event_io_map *head,                        struct event_map_entry *elm){    struct event_map_entry **p, *r;    if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit)        event_io_map_HT_GROW(head, head->hth_n_entries+1);#ifdef HT_CACHE_HASH_VALUES    do    {        (elm)->map_node.hte_hash = hashsocket(elm);    } while(0);#endif    p = _event_io_map_HT_FIND_P(head, elm);    //由前面的英文注释可知,这个函数是替换插入一起进行的。如果哈希表    //中有和elm相同的元素(指的是event_map_entry的fd成员变量值相等)    //那么就发生替代(其他成员变量值不同,所以不是完全相同,有替换意义)    //如果哈希表中没有和elm相同的元素,那么就进行插入操作    //指针p存放的是hte_next成员变量的地址    //这里的p存放的是被替换元素的前驱元素的hte_next变量地址    r = *p; //r指向了要替换的元素。有可能为NULL    *p = elm; //hte_next变量被赋予新值elm    //找到了要被替换的元素r(不为NULL)    //而且要插入的元素地址不等于要被替换的元素地址    if (r && (r!=elm))    {        elm->map_node.hte_next = r->map_node.hte_next;        r->map_node.hte_next = NULL;        return r; //返回被替换掉的元素    }    else //进行插入操作    {        //这里貌似有一个bug。如果前一个判断中,r 不为 NULL,而且r == elm        //对于同一个元素,多次调用本函数。就会出现这样情况。        //此时,将会来到这个else里面        //实际上没有增加元素,但元素的个数却被++了。因为r 的地址等于 elm        //所以 r = *p; *p = elm; 等于什么也没有做。(r == elm)//当然,因为这些函数都是Libevent内部使用的。如果它保证不会同于同//一个元素调用本函数,那么就不会出现bug        ++head->hth_n_entries;        return NULL; //插入操作返回NULL,表示没有替换到元素    }}
/* Remove any element matching 'elm' from the table 'head'.  If such * an element is found, return it; otherwise return NULL. */static inline struct event_map_entry *event_io_map_HT_REMOVE(struct event_io_map *head,                       struct event_map_entry *elm){    struct event_map_entry **p, *r;#ifdef HT_CACHE_HASH_VALUES    do    {        (elm)->map_node.hte_hash = hashsocket(elm);    } while (0);#endif    p = _event_io_map_HT_FIND_P(head,elm);    if (!p || !*p)//没有找到        return NULL;    //指针p存放的是hte_next成员变量的地址    //这里的p存放的是被替换元素的前驱元素的hte_next变量地址    r = *p; //r现在指向要被删除的元素    *p = r->map_node.hte_next;    r->map_node.hte_next = NULL;    --head->hth_n_entries;    return r;}/* Invoke the function 'fn' on every element of the table 'head', using 'data' as its second argument.  If the function returns nonzero, remove the most recently examined element before invoking the function again. */static inline voidevent_io_map_HT_FOREACH_FN(struct event_io_map *head,                           int (*fn)(struct event_map_entry *, void *),                           void *data){    unsigned idx;    struct event_map_entry **p, **nextp, *next;    if (!head->hth_table)        return;    for (idx=0; idx < head->hth_table_length; ++idx)    {        p = &head->hth_table[idx];        while (*p)        {            //像A->B->C链表。p存放了A元素中hte_next变量的地址            //*p则指向B元素。nextp存放B的hte_next变量的地址            //next指向C元素。            nextp = &(*p)->map_node.hte_next;            next = *nextp;            //对B元素进行检查            if (fn(*p, data))            {                --head->hth_n_entries;                //p存放了A元素中hte_next变量的地址                //所以*p = next使得A元素的hte_next变量值被赋值为                //next。此时链表变成A->C。即使抛弃了B元素。不知道                //调用方是否能释放B元素的内存。                *p = next;            }            else            {                p = nextp;            }        }    }}/* Return a pointer to the first element in the table 'head', under * an arbitrary order.  This order is stable under remove operations, * but not under others. If the table is empty, return NULL. *///获取第一条冲突链的第一个元素static inline struct event_map_entry **event_io_map_HT_START(struct event_io_map *head){    unsigned b = 0;    while (b < head->hth_table_length)    {        //返回哈希表中,第一个不为NULL的节点        //即有event_map_entry元素的节点。        //找到链。因为是哈希。所以不一定哈希表中的每一个节点都存有元素        if (head->hth_table[b])            return &head->hth_table[b];        ++b;    }    return NULL;}/* Return the next element in 'head' after 'elm', under the arbitrary * order used by HT_START.  If there are no more elements, return * NULL.  If 'elm' is to be removed from the table, you must call * this function for the next value before you remove it. */static inline struct event_map_entry **event_io_map_HT_NEXT(struct event_io_map *head,                     struct event_map_entry **elm){    //本哈希解决冲突的方式是链地址    //如果参数elm所在的链地址中,elm还有下一个节点,就直接返回下一个节点    if ((*elm)->map_node.hte_next)    {        return &(*elm)->map_node.hte_next;    }    else //否则取哈希表中的下一条链中第一个元素    {#ifdef HT_CACHE_HASH_VALUES        unsigned b = (((*elm)->map_node.hte_hash)                      % head->hth_table_length) + 1;#else        unsigned b = ( (hashsocket(*elm)) % head->hth_table_length) + 1;#endif        while (b < head->hth_table_length)        {            //找到链。因为是哈希。所以不一定哈希表中的每一个节点都存有元素            if (head->hth_table[b])                return &head->hth_table[b];            ++b;        }        return NULL;    }}//功能同上一个函数。只是参数不同,另外本函数还会使得--hth_n_entries//该函数主要是返回elm的下一个元素。并且哈希表的总元素个数减一。//主调函数会负责释放*elm指向的元素。无需在这里动手//在evmap_io_clear函数中,会调用本函数。static inline struct event_map_entry **event_io_map_HT_NEXT_RMV(struct event_io_map *head,                         struct event_map_entry **elm){#ifdef HT_CACHE_HASH_VALUES    unsigned h = ((*elm)->map_node.hte_hash);#else    unsigned h = (hashsocket(*elm));#endif    //elm变量变成存放下一个元素的hte_next的地址    *elm = (*elm)->map_node.hte_next;    --head->hth_n_entries;    if (*elm)    {        return elm;    }    else    {        unsigned b = (h % head->hth_table_length)+1;        while (b < head->hth_table_length)        {            if (head->hth_table[b])                return &head->hth_table[b];            ++b;        }        return NULL;    }}//素数表。之所以去素数,是因为在取模的时候,素数比合数更有优势。//听说是更散,更均匀static unsigned event_io_map_PRIMES[] ={    //素数表的元素具有差不多2倍的关系。    //这使得扩容操作不会经常发生。每次扩容都预留比较大的空间    53, 97, 193, 389, 769, 1543, 3079,    6151, 12289, 24593, 49157, 98317,    196613, 393241, 786433, 1572869, 3145739,    6291469, 12582917, 25165843, 50331653, 100663319,    201326611, 402653189, 805306457, 1610612741};//素数表中,元素的个数。static unsigned event_io_map_N_PRIMES =        (unsigned)(sizeof(event_io_map_PRIMES)                   /sizeof(event_io_map_PRIMES[0]));/* Expand the internal table of 'head' until it is large enough to * hold 'size' elements.  Return 0 on success, -1 on allocation * failure. */int event_io_map_HT_GROW(struct event_io_map *head, unsigned size){    unsigned new_len, new_load_limit;    int prime_idx;    struct event_map_entry **new_table;    //已经用到了素数表中最后一个素数,不能再扩容了。    if (head->hth_prime_idx == (int)event_io_map_N_PRIMES - 1)        return 0;    //哈希表中还够容量,无需扩容    if (head->hth_load_limit > size)        return 0;    prime_idx = head->hth_prime_idx;    do {        new_len = event_io_map_PRIMES[++prime_idx];        //从素数表中的数值可以看到,后一个差不多是前一个的2倍。        //从0.5和后的new_load_limit <= size,可以得知此次扩容        //至少得是所需大小(size)的2倍以上。免得经常要进行扩容        new_load_limit = (unsigned)(0.5*new_len);    } while (new_load_limit <= size             && prime_idx < (int)event_io_map_N_PRIMES);    if ((new_table = mm_malloc(new_len*sizeof(struct event_map_entry*))))    {        unsigned b;        memset(new_table, 0, new_len*sizeof(struct event_map_entry*));        for (b = 0; b < head->hth_table_length; ++b)        {            struct event_map_entry *elm, *next;            unsigned b2;            elm = head->hth_table[b];            while (elm) //该节点有冲突链。遍历冲突链中所有的元素            {                next = elm->map_node.hte_next;                //冲突链中的元素,相对于前一个素数同余(即模素数后,结果相当)                //但对于现在的新素数就不一定同余了,即它们不一定还会冲突                //所以要对冲突链中的所有元素都再次哈希,并放到它们应该在的地方                //b2存放了再次哈希后,元素应该存放的节点下标。#ifdef HT_CACHE_HASH_VALUES                b2 = (elm)->map_node.hte_hash % new_len;#else                b2 = (hashsocket(*elm)) % new_len;#endif                //用头插法插入数据                elm->map_node.hte_next = new_table[b2];                new_table[b2] = elm;                elm = next;            }        }        if (head->hth_table)            mm_free(head->hth_table);        head->hth_table = new_table;    }    else    {        unsigned b, b2;        //刚才mm_malloc失败,可能是内存不够。现在用更省内存的        //mm_realloc方式。当然其代价就是更耗时(下面的代码可以看到)。        //前面的mm_malloc会同时有hth_table和new_table两个数组。        //而mm_realloc则只有一个数组,所以省内存,省了一个hth_table数组        //的内存。此时,new_table数组的前head->hth_table_length个        //元素存放了原来的冲突链的头部。也正是这个原因导致后面代码更耗时。        //其实,只有在很特殊的情况下,这个函数才会比mm_malloc省内存.        //就是堆内存head->hth_table区域的后面刚好有一段可以用的内存。        //具体的,可以搜一下realloc这个函数。        new_table = mm_realloc(head->hth_table,                               new_len*sizeof(struct event_map_entry*));        if (!new_table)            return -1;        memset(new_table + head->hth_table_length, 0,               (new_len - head->hth_table_length)*sizeof(struct event_map_entry*)               );        for (b=0; b < head->hth_table_length; ++b)        {            struct event_map_entry *e, **pE;            for (pE = &new_table[b], e = *pE; e != NULL; e = *pE)            {#ifdef HT_CACHE_HASH_VALUES                b2 = (e)->map_node.hte_hash % new_len;#else                b2 = (hashsocket(*elm)) % new_len;#endif                //对于冲突链A->B->C.                //pE是二级指针,存放的是A元素的hte_next指针的地址值                //e指向B元素。                //对新的素数进行哈希,刚好又在原来的位置                if (b2 == b)                {                    //此时,无需修改。接着处理冲突链中的下一个元素即可                    //pE向前移动,存放B元素的hte_next指针的地址值                    pE = &e->map_node.hte_next;                }                else//这个元素会去到其他位置上。                {                    //此时冲突链修改成A->C。                    //所以pE无需修改,还是存放A元素的hte_next指针的地址值                    //但A元素的hte_next指针要指向C元素。用*pE去修改即可                    *pE = e->map_node.hte_next;                    //将这个元素放到正确的位置上。                    e->map_node.hte_next = new_table[b2];                    new_table[b2] = e;                }                //这种再次哈希的方式,很有可能会对某些元素操作两次。                //当某个元素第一次在else中处理,那么它就会被哈希到正确的节点                //的冲突链上。随着外循环的进行,处理到正确的节点时。在遍历该节点                //的冲突链时,又会再次处理该元素。此时,就会在if中处理。而不会                //进入到else中。            }        }        head->hth_table = new_table;    }    //一般是当hth_n_entries >= hth_load_limit时,就会调用    //本函数。hth_n_entries表示的是哈希表中节点的个数。而hth_load_limit    //是hth_table_length的一半。hth_table_length则是哈希表中    //哈希函数被模的数字。所以,当哈希表中的节点个数到达哈希表长度的一半时    //就会发生增长,本函数被调用。这样的结果是:平均来说,哈希表应该比较少发生    //冲突。即使有,冲突链也不会太长。这样就能有比较快的查找速度。    head->hth_table_length = new_len;    head->hth_prime_idx = prime_idx;    head->hth_load_limit = new_load_limit;    return 0;}/* Free all storage held by 'head'.  Does not free 'head' itself, * or individual elements. 并不需要释放独立的元素*///在evmap_io_clear函数会调用该函数。其是在删除所有哈希表中的元素后//才调用该函数的。void event_io_map_HT_CLEAR(struct event_io_map *head){    if (head->hth_table)        mm_free(head->hth_table);    head->hth_table_length = 0;    event_io_map_HT_INIT(head);}/* Debugging helper: return false iff the representation of 'head' is * internally consistent. */int _event_io_map_HT_REP_IS_BAD(const struct event_io_map *head){    unsigned n, i;    struct event_map_entry *elm;    if (!head->hth_table_length)    {        //刚被初始化,还没申请任何空间        if (!head->hth_table && !head->hth_n_entries                && !head->hth_load_limit && head->hth_prime_idx == -1                )            return 0;        else            return 1;    }    if (!head->hth_table || head->hth_prime_idx < 0            || !head->hth_load_limit            )        return 2;    if (head->hth_n_entries > head->hth_load_limit)        return 3;    if (head->hth_table_length != event_io_map_PRIMES[head->hth_prime_idx])        return 4;    if (head->hth_load_limit != (unsigned)(0.5*head->hth_table_length))        return 5;    for (n = i = 0; i < head->hth_table_length; ++i)    {        for (elm = head->hth_table[i]; elm; elm = elm->map_node.hte_next)        {#ifdef HT_CACHE_HASH_VALUES            if (elm->map_node.hte_hash != hashsocket(elm))                return 1000 + i;            if( (elm->map_node.hte_hash % head->hth_table_length) != i)                return 10000 + i;#else            if ( (hashsocket(*elm)) != hashsocket(elm))                return 1000 + i;            if( ( (hashsocket(*elm)) % head->hth_table_length) != i)                return 10000 + i;#endif            ++n;        }    }    if (n != head->hth_n_entries)        return 6;    return 0;}


   在event_base这个结构体中有一个event_io_map类型的成员变量io。它就是一个哈希表。当一个监听读或者写操作的event,调用event_add函数插入到event_base中时,就会调用evmap_io_add函数。evmap_io_add函数应用到这个event_io_map结构体。该函数的定义如下,其中使用到了一个宏定义,


/* return -1 on error, 0 on success if nothing changed in the event backend, * and 1 on success if something did. */intevmap_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;#ifndef EVMAP_USE_HTif (fd >= io->nentries) {if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)return (-1);}#endifGET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init, evsel->fdinfo_len);<span style="font-family:Arial;color:#333333;"><span style="font-size: 14px; line-height: 26px;">                                                                                                                                       该宏定义内部新创建并初始化一个event_map_entry,并通过一个哈希算法将该对象存放到哈希表一个合适的位置。之后通过TAILQ_INSERT_TAIL方法,将event对象附加到event的列表上。//在哈希数组中如果已经存在一个event_map_entry,则返回该结构体内的evmap_io,如果不存在则要//新建一个,并返回该结构内的evmap_io。evmap_io 结构体指针存储在ctx中。</span></span>        <span style="white-space:pre"></span>do {<span style="white-space:pre"></span>\<span style="white-space:pre"></span>struct event_map_entry key_, *ent_;<span style="white-space:pre"></span>\<span style="white-space:pre"></span>key_.fd = slot;<span style="white-space:pre"></span>\<span style="white-space:pre"></span>HT_FIND_OR_INSERT_(event_io_map, map_node, hashsocket, map, \<span style="white-space:pre"></span>    event_map_entry, &key_, ptr,<span style="white-space:pre"></span>\<span style="white-space:pre"></span>    {<span style="white-space:pre"></span>\<span style="white-space:pre"></span>    ent_ = *ptr;<span style="white-space:pre"></span>\<span style="white-space:pre"></span>    },<span style="white-space:pre"></span>\<span style="white-space:pre"></span>    {<span style="white-space:pre"></span>\<span style="white-space:pre"></span>    ent_ = mm_calloc(1,sizeof(struct event_map_entry)+fdinfo_len); \<span style="white-space:pre"></span>    if (EVUTIL_UNLIKELY(ent_ == NULL))<span style="white-space:pre"></span>\<span style="white-space:pre"></span>    return (-1);<span style="white-space:pre"></span>\<span style="white-space:pre"></span>    ent_->fd = slot;<span style="white-space:pre"></span>\<span style="white-space:pre"></span>    (ctor)(&ent_->ent.type);<span style="white-space:pre"></span>\<span style="white-space:pre"></span>    HT_FOI_INSERT_(map_node, map, &key_, ent_, ptr) \<span style="white-space:pre"></span>});<span style="white-space:pre"></span>\<span style="white-space:pre"></span>(x) = &ent_->ent.type;<span style="white-space:pre"></span>\<span style="white-space:pre"></span>} while (0)                   nread = ctx->nread;读事件个数nwrite = ctx->nwrite;写事件个数nclose = ctx->nclose;<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">  //如果有读或写的事件存在,在临时变量中保存该事件类型。</span>if (nread)old |= EV_READ;if (nwrite)old |= EV_WRITE;if (nclose)old |= EV_CLOSED;<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">    //如果是读事件</span>if (ev->ev_events & EV_READ) {if (++nread == 1)<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"> //如果是第一次读则纪录该事件类型为读事件,并保存在临时变量res中。</span>res |= EV_READ;}if (ev->ev_events & EV_WRITE) {if (++nwrite == 1)<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">    //如果是第一次读则纪录该事件类型为读事件,并保存在临时变量res中。</span>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);<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">  //如果超过65535个度或写事件则返回异常</span>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) {<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"> //如果是第一次增加读或写事件</span>void *extra = ((char*)ctx) + sizeof(struct evmap_io);/* XXX(niels): we cannot mix edge-triggered and * level-triggered, we should probably assert on * this. */<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">  //将事件增加到操作系统的监听里<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 16px; line-height: 24px;">  //把fd加入到多路IO复用中。  </span><span style="margin: 0px; padding: 0px; border: none; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 16px; line-height: 24px;"> </span></span>if (evsel->add(base, ev->ev_fd,old, (ev->ev_events & EV_ET) | res, extra) == -1)return (-1);retval = 1;}<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"> //记录读写次数</span>ctx->nread = (ev_uint16_t) nread;ctx->nwrite = (ev_uint16_t) nwrite;ctx->nclose = (ev_uint16_t) nclose;                                                                                                                              //ctx->events是一个TAILQ_HEAD。结合之前讲到的TAILQ_QUEUE队列,      //就可以知道:同一个fd,可能有多个event结构体。这里就把这些结构体连      //起来。依靠的链表是,event结构体中的ev_io_next。ev_io_next是      //一个TAILQ_ENTRY,具有前驱和后驱指针。队列头部为event_map_entry      //结构体中的evmap_io成员的events成员。  LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">    //将该事件增加到evmap_io内的Tail队列中。</span>return (retval);}

event_signal_map()

分析:

//TAILQ_HEAD (event_list, event); event_struct.hstruct event_list{struct event *tqh_first;struct event **tqh_last;};struct evmap_signal {struct event_list events;};struct event_signal_map {/* An array of evmap_io * or of evmap_signal *; empty entries are * set to NULL. */void **entries; //二级指针,evmap_signal*数组int nentries; //元素个数};
 相对于event_io_map是一个哈希表,event_signal_map是一个数组。并且entries虽然是void**,但实际它是一个structevmap_signal指针数组。而evmap_signal成员只有一个TAILQ_HEAD (event_list, event);

event_signal_map结构体指向了一个evmap_signal指针数组,而evmap_signal结构体可以说就是一个TAILQ_QUEUE队列的队列头。通过TAILQ_QUEUE队列和图中的结构,event_signal_map就可以把event结构体都关联并管理起来。

        上图中会有一个event结构体队列,因为同一个文件描述符fd或者信号值sig是可以多次调用event_new、event_add函数的。即存在一对多。这个队列就是把同一个fd或者sig的event连起来,方便管理。


//slot是信号值sig,或者文件描述符fd.//当sig或者fd >= map的nentries变量时就会调用此函数static int   //msize 等于 sizeof(struct evmap_signal *)evmap_make_space(struct event_signal_map *map, int slot, int msize){    if (map->nentries <= slot) {        //posix标准中,信号的种类就只有32种。           //所以一开始取32还是比较合理的        int nentries = map->nentries ? map->nentries : 32;        void **tmp;        //当slot是一个文件描述符时,就会大于32        while (nentries <= slot)            nentries <<= 1;        tmp = (void **)mm_realloc(map->entries, nentries * msize);        if (tmp == NULL)            return (-1);        //清零是很有必要的。因为tmp是二级指针,数组里面的元素是一个指针        memset(&tmp[map->nentries], 0,            (nentries - map->nentries) * msize);        map->nentries = nentries;        map->entries = tmp;    }    return (0);}

intevmap_signal_add(struct event_base *base, int sig, struct event *ev){    const struct eventop *evsel = base->evsigsel;    struct event_signal_map *map = &base->sigmap;    struct evmap_signal *ctx = NULL;    if (sig >= map->nentries) {        if (evmap_make_space(map, sig, sizeof(struct evmap_signal *)) == -1)            return (-1);    }    //无论是GET_SIGNAL_SLOT_AND_CTOR还是GET_IO_SLOT_AND_CTOR,其作用    //都是在数组(哈希表也是一个数组)中找到fd中的一个结构。    //GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,    //base->evsigsel->fdinfo_len);      GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,base->evsigsel->fdinfo_len);     if (LIST_EMPTY(&ctx->events)) {<span style="white-space:pre"></span>if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)<span style="white-space:pre"></span>    == -1)<span style="white-space:pre"></span>return (-1);<span style="white-space:pre"></span>}<span style="white-space:pre"></span>LIST_INSERT_HEAD(&ctx->events, ev, ev_signal_next);    //将所有有相同信号值的event连起来    TAILQ_INSERT_TAIL(&ctx->events, ev, ev_signal_next);    return (1);}

static voidevent_queue_insert_inserted(struct event_base *base, struct event *ev){EVENT_BASE_ASSERT_LOCKED(base);if (EVUTIL_FAILURE_CHECK(ev->ev_flags & EVLIST_INSERTED)) {event_errx(1, "%s: %p(fd "EV_SOCK_FMT") already inserted", __func__,    ev, EV_SOCK_ARG(ev->ev_fd));return;}INCR_EVENT_COUNT(base, ev->ev_flags);ev->ev_flags |= EVLIST_INSERTED;  改变状态 加入insert}



0 0
原创粉丝点击