Libevent源码分析-----event_io_map哈希表

        转载请注明出处: http://blog.csdn.net/luotuo44/article/details/38403241



        上一篇博客说到了TAILQ_QUEUE队列,它可以把多个event结构体连在一起。是一种归类方式。本文也将讲解一种将event归类、连在一起的结构:哈希结构。


哈希结构体:

        哈希结构由下面几个结构体一起配合工作:

struct event_list
{
    struct event *tqh_first;
    struct event **tqh_last;
};

struct evmap_io {
	//TAILQ_HEAD (event_list, event);
	struct event_list events;
	ev_uint16_t nread;
	ev_uint16_t nwrite;
};

struct event_map_entry {
	HT_ENTRY(event_map_entry) map_node; //next指针
	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;
};

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。这正是前一篇博客说到的TAILQ_QUEUE队列的队列头。从这一点可以看到,这个哈希结构还是比较复杂的。

        为什么在哈希表的元素里面,还会有一个TAILQ_QUEUE队列呢?这得由Libevent的一个特征说起。Libevent允许用同一个文件描述符fd或者信号值,调用event_new、event_add多次。所以,同一个fd或者信号值就可以对应多个event结构体了。所以这个TAILQ_QUEUE队列就是将这些具有相同fd或者信号值的多个event结构体连一起。

 

什么情况会使用哈希表:

        有一点需要说明,那就是Libevent中的哈希表只会用于Windows系统,像遵循POSIX标准的OS是不会用到哈希表的。从下面的定义可以看到这一点。

//event-internal.h文件
#ifdef WIN32
/* If we're on win32, then file descriptors are not nice low densely packed
   integers.  Instead, they are pointer-like windows handles, and we want to
   use a hashtable instead of an array to map fds to events.
*/
#define EVMAP_USE_HT
#endif


#ifdef EVMAP_USE_HT
#include "ht-internal.h"
struct event_map_entry;
HT_HEAD(event_io_map, event_map_entry);
#else
#define event_io_map event_signal_map
#endif

        可以看到,如果是非Windows系统,那么event_io_map就会被定义成event_signal_map(这是一个很简单的结构)。而在Windows系统,那么就由HT_HEAD这个宏定义event_io_map。最后得到的event_io_map就是本文最前面所示的那样。

        为什么只有在Windows系统才会使用这个哈希表呢?代码里面的注释给出了一些解释。因为在Windows系统里面,文件描述符是一个比较大的值,不适合放到event_signal_map结构中。而通过哈希(模上一个小的值),就可以变得比较小,这样就可以放到哈希表的数组中了。而遵循POSIX标准的文件描述符是从0开始递增的,一般都不会太大,适用于event_signal_map。


哈希函数:

        前面说到哈希函数是 用文件描述符fd 模 哈希表的长度。实际上,并不是直接用fd,而是用一个叫hashsocket的函数将这个fd进行一些处理后,才去模 哈希表的长度。下面是hashsocket函数的实现:

//evmap.c文件
/* Helper used by the event_io_map hashtable code; tries to return a good hash
 * of the fd in e->fd. */
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_map_entry结构体中,还有一个HT_ENTRY的宏。从名字来看,它是一个哈希表的表项。它是一个条件宏,定义如下:

//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	
        可以看到,如果定义了HT_CACHE_HASH_VALUES宏,那么就会多一个hte_hash变量。从宏的名字来看,这是一个cache。不错,变量hte_hash就是用来存储前面的hashsocket的返回值。当第一次计算得到后,就存放到hte_hash变量中。以后需要用到(会经常用到),就直接向这个变量要即可,无需再次计算hashsocket函数。如果没有这个变量,那么需要用到这个值,都要调用hashsocket函数计算一次。这一点从后面的代码可以看到。



哈希表操作函数:

     

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值