win32有哈希表,linux没有
在 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
#define HT_NO_CACHE_HASH_VALUES
#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
追踪 宏EVMAP_USE_HT 可以得知,基于哈希表的结构来存放event只有在windows平台才有,在linux下是没有的。
在Windows,文件描述符是一个比较大的值,不适合放到event_signal_map结构中。通过哈希(模上一个小的值),就可以变得比较小,这样就可以放到哈希表的数组中了。
遵循POSIX标准的文件描述符是从0开始递增的,一般都不会太大,io事件和信号事件都是使用 event_signal_map ,只不过#define event_io_map event_signal_map
,通过一个宏定义来统一命名而已。
event_map_entry
libevent2.1后,原来的 event_io_map 改为了 event_map_entry ,里面的成员变量也拆分成在不同的文件定义了。包括的文件有:ht-internal.h,evmap.h
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_ENTRY
#ifdef HT_NO_CACHE_HASH_VALUES
#define HT_ENTRY(type) \
struct { \
struct type *hte_next; \
}
#else
#define HT_ENTRY(type) \
struct { \
struct type *hte_next; \
unsigned hte_hash; \
}
#endif
那么把各处的成员变量合在一个结构就是:
struct event_map_entry {
struct { \
struct type *hte_next; \
unsigned hte_hash; \
}
evutil_socket_t fd;
union {
struct evmap_io evmap_io;
} ent;
} **由上面那些结构体配合得到的哈希表结构如下图所示**
event_signal_map
struct evmap_signal {
struct event_dlist events;
};
信号事件的存放是在一个动态数组中的,并且每个槽连接一条链表,用来存放相同监听相同signal的事件。
由上面那些结构体配合得到的event_signal_map结构如下图所示
哈希表的三个重点
- 哈希函数
- 哈希冲突(拉链法)
- 扩容
- 缩容
事件加入event_base时
上面都说到event是存放在map中的,那么看下event_add的流程:
- 1.event_add
- 2.ent_add_nolock_
- 3.evmap_io_add_ 或者 evmap_signal_add_
- 4.GET_IO_SLOT_AND_CTOR
第三步这里详细流程是:
- 1.判断事件的类型,是读写,关闭,信号事件,非插入,激活,激活更新事件
- 2.如果是io事件,调用 evmap_io_add_
-
3.如果是信号事件,调用 evmap_signal_add_
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) && !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) { 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 != -1) event_queue_insert_inserted(base, ev); if (res == 1) { /* evmap says we need to notify the main thread. */ notify = 1; res = 0; } }
追踪源码的时候发现一个觉得需要注意一下的:
if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff || nclose > 0xffff)) {
event_warnx("Too many events reading or writing on fd %d",
(int)fd);
return -1;
}
一个fd可以重复设置事件,但是不能大于 0xffff 个事件。
libevent的五种据结构
- ngly-linkedists(单链表
- lists(双向链表)
- simple queues(单向队列)
- tail queues(尾队列)
- circular queues (循环队列)