Libevent源码分析-----evbuffer结构与基本操作

本文详细分析了Libevent中的evbuffer结构体及其操作,包括在链表尾添加数据、预留buffer空间、在链表头添加数据和读取数据等操作。Libevent的buffer采用evbuffer_chain组成的链表来存储数据,并通过evbuffer_add、evbuffer_prepend等函数进行数据管理。文章还探讨了evbuffer_expand在预留空间上的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

        

        对于非阻塞IO的网络库来说,buffer几乎是必须的。Libevent在1.0版本之前就提供了buffer功能。现在来看一下Libevent的buffer。


buffer相关结构体:

        Libevent为buffer定义了下面的结构体:

//evbuffer-internal.h文件
struct evbuffer_chain;
struct evbuffer {
	struct evbuffer_chain *first;
	struct evbuffer_chain *last;
	//这是一个二级指针。使用*last_with_datap时,指向的是链表中最后一个有数据的evbuffer_chain。
	//所以last_with_datap存储的是倒数第二个evbuffer_chain的next成员地址。
	//一开始buffer->last_with_datap = &buffer->first;此时first为NULL。所以当链表没有节点时
	//*last_with_datap为NULL。当只有一个节点时*last_with_datap就是first。	
	struct evbuffer_chain **last_with_datap;

	size_t total_len;//链表中所有chain的总字节数

	...
};


struct evbuffer_chain {
	struct evbuffer_chain *next;
	size_t buffer_len;//buffer的大小

	//错开不使用的空间。该成员的值一般等于0
	ev_off_t misalign;

	//evbuffer_chain已存数据的字节数
	//所以要从buffer + misalign + off的位置开始写入数据
	size_t off;

	...
	
	unsigned char *buffer;
};

        这两个结构体配合工作得到下图所示的存储结构:

        

        因为last_with_datap成员比较特殊,上图只是展示了一种情况。后面还有一张图,展示另外一种情况。

        Libevent将缓冲数据都存放到buffer中。通过一个个的evbuffer_chain连成的链表可以存放很多的缓冲数据。

        这是一个很常见的链表形式。但Libevent有一个很独特的地方,就是那个evbuffer_chain结构体。

        首先,该结构体有misalign成员。该成员表示错开不用的buffer空间。也就是说buffer中真正的数据是从buffer + misalign开始。

        第二,evbuffer_chain结构体buffer是一个指针,按道理来说,应该单独调用malloc分配一个堆内存并让buffer指向之。但实际上buffer指向的内存和evbuffer_chain结构体本身的存储内存是一起分配的。下面代码展示了这一点:

//evbuffer-internal.h文件
#define EVBUFFER_CHAIN_SIZE sizeof(struct evbuffer_chain)

#if _EVENT_SIZEOF_VOID_P < 8
#define MIN_BUFFER_SIZE	512
#else
#define MIN_BUFFER_SIZE	1024
#endif

//宏的作用就是返回,chain + sizeof(evbuffer_chain) 的内存地址。
#define EVBUFFER_CHAIN_EXTRA(t, c) (t *)((struct evbuffer_chain *)(c) + 1)


//buffer.c文件
static struct evbuffer_chain *
evbuffer_chain_new(size_t size)//size是buffer所需的大小
{
	struct evbuffer_chain *chain;
	size_t to_alloc;

	//所需的大小size 再 加上evbuffer_chain结构体本身所需
	//的内存大小。这样做的原因是,evbuffer_chain本身是管理
	//buffer的结构体。但buffer内存就分配在evbuffer_chain结构体存储
	//内存的后面。所以要申请多一些内存。
	size += EVBUFFER_CHAIN_SIZE;//evbuffer_chain结构体本身的大小

	
	to_alloc = MIN_BUFFER_SIZE; //内存块的最小值
	while (to_alloc < size)
		to_alloc <<= 1;
   //从分配的内存大小可以知道,evbuffer_chain结构体和buffer是一起分配的
	//也就是说他们是存放在同一块内存中
	if ((chain = mm_malloc(to_alloc)) == NULL)
		return (NULL);

	//只需初始化最前面的结构体部分即可
	memset(chain, 0, EVBUFFER_CHAIN_SIZE);

	//buffer_len存储的是buffer的大小
	chain->buffer_len = to_alloc - EVBUFFER_CHAIN_SIZE;

	 //宏的作用就是返回,chain + sizeof(evbuffer_chain) 的内存地址。
	 //其效果就是buffer指向的内存刚好是在evbuffer_chain的后面。
	chain->buffer = EVBUFFER_CHAIN_EXTRA(u_char, chain);

	return (chain);
}

        前面的图中,buffer内存区域(蓝色区域)连在next的后面也是基于这一点的。在代码的while循环中也可以看到申请的空间大小是512的倍数,也就是说evbuffer_chain申请的空间大小是512、1024、2048、4096……

 

        上面贴出了函数evbuffer_chain_new,该函数是用来创建一个evbuffer_chain。现在贴出另外一个函数evbuffer_new,它是用来创建一个evbuffer的。

//buffer.c
struct evbuffer *
evbuffer_new(void)
{
	struct evbuffer *buffer;

	buffer = mm_calloc(1, sizeof(struct evbuffer));
	if (buffer == NULL)
		return (NULL);

	buffer->refcnt = 1;
	buffer->last_with_datap = &buffer->first;

	return (buffer);
}


Buffer的数据操作:

在链表尾添加数据:

        

        Libevent提供给用户的添加数据接口是evbuffer_add,现在就通过这个函数看一下是怎么将数据插入到buffer中的。该函数是在链表的尾部添加数据,如果想在链表的前面添加数据可以使用evbuffer_prepend。在链表尾部插入数据,分下面几种情况:

  1. 该链表为空,即这是第一次插入数据。这是最简单的,直接把新建的evbuffer_chain插入到链表中,通过调用evbuffer_chain_insert。
  2. 链表的最后一个节点(即evbuffer_chain)还有一些空余的空间,放得下本次要插入的数据。此时直接把数据追加到最后一个节点即可。
  3. 链表的最后一个节点并不能放得下本次要插入的数据,那么就需要把本次要插入的数据分开由两个evbuffer_chain存放。

        具体的实现如下面所示:

//buffer.c文件
int
evbuffer_add(struct evbuffer *buf, const void *data_in, size_t datlen)
{
	struct evbuffer_chain *chain, *tmp;
	const unsigned char *data = data_in;
	size_t remain, to_alloc;
	int result = -1;

	EVBUFFER_LOCK(buf);//加锁,线程安全

	//冻结缓冲区尾部,禁止追加数据
	if (buf->freeze_end) {
		goto done;
	}

	//找到最后一个evbuffer_chain。
	chain = buf->last;

	//第一次插入数据时,
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值