0 参考
http://blog.csdn.net/column/details/libevent-src.html?&page=1
Libevent源码中queue.h包含了链表的实现。在学习前参考了内核链表的设计,基本理解了queue.h的设计初衷。
1 内核链表
内核链表: https://www.ibm.com/developerworks/cn/linux/kernel/l-chain/
内核的链表(List, 双向链表)为了“通用性”,避免了为每个数据项类型定义自己的链表的麻烦,将链表节点结构“嵌入”到需要链表的struct中使用,然后通过计算偏移量来得到struct的起始地址。
双向链表拥有两个指针,对于HASH表来说太浪费,所以为了优化,又提出了hlist,即单单指针表头双循环链表。
因为表头和节点的数据结构不同,插入操作如果发生在表头和首节点之间,以往的方法就行不通了:表头的first指针必须修改指向新插入的节点,却不能使用类似list_add()
这样统一的描述。为此,hlist节点的prev不再是指向前一个节点的指针,而是指向前一个节点(可能是表头)中的next(对于表头则是first)指针(struct list_head
*pprev),从而在表头插入的操作可以通过一致的”(node->pprev)”访问和修改前驱节点的next(或first)指针。
关于hlist可以参考这篇文章,解释了为什么使用二级指针:
2 libevent中的list
上面的文章分析了TAILQ_LIST实现原理,重点也是理解二级指针的使用。
3 libevent中的hash表
对于hash表,关键是要明确hsah函数,冲突解决方法。libevent在Linux平台使用fd作为index,在window平台需要根据HANDLE计算出index;冲突解决方法都是:拉链法。
4 libevent中的evbuffer
nonblock网络库不能没有缓冲区,evbuffer可以用作发送缓冲区和接收缓冲区。关键是,它还支持用户回掉。
evbuffer有一个回调函数队列成员callbacks,向evbuffer删除或者添加数据时,就会调用这些回调函数。之所以是回调函数队列,是因为一个evbuffer是可以添加多个回调函数的,而且同一个回调函数可以被添加多次。
使用回调函数时有一点要注意:因为当evbuffer被添加或者删除数据时,就会调用这些回调函数,所以在回调函数里面不要添加或者删除数据,不然将导致递归,死循环。
evbuffer的回调函数对bufferevent来说是非常重要的,bufferevent的一些重要功能都是基于evbuffer的回调函数完成的。
5 libevent中的bufferevent
个人认为bufferevent是libevent中最闪耀的地方,因为它很好的简化了发送和接收操作。如果你想发送数据,把数据写到bufferevent的发送缓冲区就好了,libevent会负责将数据发送出去。如果你想在至少有N字节数据时才通知你(调用你的callback), 那么你设置bufferevent的input buffer的低水位为N就可以了,libevent只有在收到N或N以上字节数据时才会通知你。如果你担心接收缓冲区不断增长会耗尽内存,那么可以设置input buffer的高水位为Max, 当inputbuffer数据大于等于Max时,libevent 会关闭对可读事件的关注,直到你从inputbuffer取出了一些数据,使得inputbuffer数据低于高水位时,libevent才会继续关注可读事件,并将内核中接收缓冲区的数据读到inputbuffer中。
bufferevent会向event_base注册两个event: read event 和 write event,同时注册bufferevent自己的readcb和writecb,这两个函数用户无需关心。
bufferevent还持有用户设置的读低水位回掉read_lowcb、写低水位回掉write_lowcb
bufferevent还有有两个evbuffer缓冲区:inputbuffer和 outputbuffer。
readcb在读取的数据大于等于读低水位时,会调用read_lowcb。因为默认读低水位为0,所以只要inputbuffer有数据,用户read_lowcb就会被调用。
readcb在读取的数据大于等于读高水位时,会删除注册的读事件。这样libevent就不会把内核接收缓冲区中的数据读到inputbuffer,避免inputbuffer继续增长。当用户从inputbuffer 消耗了一些数据时,inputbuffer的callback会被触发,该callback会检查inputbuffer是否小于高水位,如果小于高水位,该callback会恢复对可读事件的关注。
当用户想outputbuffer写入数据时,libevent负责将数据发送出去,而outputbuffer每次发生变化时,如果outputbuffer的数据量小于等于写低水位时,write_lowcb会被调用。因为默认写低水位为0,所以当outputbuffer中的数据发送完之后,write_lowcb就会被调用。