Libevent源码分析-----更多evbuffer操作函数

本文详细分析了Libevent库中evbuffer的数据结构和操作,包括锁操作、查找功能、回调函数的使用及网络IO。重点讨论了evbuffer如何处理查找结构体、字符和字符串,以及读取和写入网络数据的机制。同时,文章提到了evbuffer的回调函数队列和如何避免在回调中引发递归。

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

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


锁操作:


        在 前一篇博文可以看到很多函数在操作前都需要对这个evbuffer进行加锁。同event_base不同,如果evbuffer支持锁的话,要显式地调用函数evbuffer_enable_locking。

//buffer.c文件
int//参数可以是一个锁变量也可以是NULL
evbuffer_enable_locking(struct evbuffer *buf, void *lock)
{
#ifdef _EVENT_DISABLE_THREAD_SUPPORT
	return -1;
#else
	if (buf->lock)
		return -1;

	if (!lock) {
		//自己分配锁变量
		EVTHREAD_ALLOC_LOCK(lock, EVTHREAD_LOCKTYPE_RECURSIVE);
		if (!lock)
			return -1;
		buf->lock = lock;
		//该evbuffer拥有锁,到时需要释放锁内存
		buf->own_lock = 1;
	} else {
		buf->lock = lock;//使用参数提供的锁
		buf->own_lock = 0;//自己没有拥有锁。不需要释放锁内存
	}

	return 0;
#endif
}

        可以看到,第二个参数可以为NULL。此时函数内部会申请一个锁。明显如果要让evbuffer能使用锁,就必须在一开始就调用evthread_use_windows_threads()或者evthread_use_pthreads(),关于这个可以参考一篇博文

      

        因为用户操控这个evbuffer,所以Libevent提供了加锁和解锁接口给用户使用。

//buffer.c文件
void
evbuffer_lock(struct evbuffer *buf)
{
	EVBUFFER_LOCK(buf);
}

void
evbuffer_unlock(struct evbuffer *buf)
{
	EVBUFFER_UNLOCK(buf);
}

//evbuffer-internal.h文件
#define EVBUFFER_LOCK(buffer)						\
	do {								\
		EVLOCK_LOCK((buffer)->lock, 0);				\
	} while (0)
#define EVBUFFER_UNLOCK(buffer)						\
	do {								\
		EVLOCK_UNLOCK((buffer)->lock, 0);			\
	} while (0)

        在Libevent内部,一般不会使用这个两个接口,而是直接使用EVBUFFER_LOCK(buf)和EVBUFFER_UNLOCK(buf)。




查找操作:

        下图展示了evbuffer的一些查找操作以及调用关系。

        



查找结构体:

        对于一个数组或者一个文件,只需一个下标或者偏移量就可以定位查找了。但对于evbuffer来说,它的数据是由一个个的evbuffer_chain用链表连在一起的。所以在evbuffer中定位,不仅仅要有一个偏移量,还要指明是哪个evbuffer_chain,甚至是在evbuffer_chain中的偏移量。因此Libevent定义了一个查找(定位)结构体:

//buffer.h文件
struct evbuffer_ptr {
	ev_ssize_t pos;//总偏移量,相对于数据的开始位置

	/* Do not alter the values of fields. */
	struct {
		void *chain;//指明是哪个evbuffer_chain
		size_t pos_in_chain; //在evbuffer_chain中的偏移量
	} _internal;
};

        有一点要注意,pos_in_chain是从misalign这个错开空间之后计算的,也就是说其实际偏移量为:chain->buffer+ chain->misalign + pos_in_chain。

        定位结构体有一个对应的操作函数evbuffer_ptr_set,该函数就像fseek函数那样,可以设置或者移动偏移量,并且可以绝对和相对地移动。

//buffer.h文件
enum evbuffer_ptr_how {
	EVBUFFER_PTR_SET, //偏移量是一个绝对位置
	EVBUFFER_PTR_ADD //偏移量是一个相对位置
};


//buffer.c文件
//设置evbuffer_ptr。evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET)
//将这个pos指向链表的开头
//position指明移动的偏移量,how指明该偏移量是绝对偏移量还是相对当前位置的偏移量。
int//这个函数的作用就像C语言中的fseek,设置文件指针的偏移量
evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos,
    size_t position, enum evbuffer_ptr_how how)
{
	size_t left = position;
	struct evbuffer_chain *chain = NULL;

	EVBUFFER_LOCK(buf);

	//这个switch的作用就是给pos设置新的总偏移量值。
	switch (how) {
	case EVBUFFER_PTR_SET://绝对位置
		chain = buf->first;//从第一个evbuffer_chain算起
		pos->pos = position; //设置总偏移量
		position = 0;
		break;
	case EVBUFFER_PTR_ADD://相对位置
		chain = pos->_internal.chain;//从当前evbuffer_chain算起
		pos->pos += position;//加上相对偏移量
		position = pos->_internal.pos_in_chain;
		break;
	}

	//这个偏移量跨了evbuffer_chain。可能不止跨一个chain。
	while (chain && position + left >= chain->off) {
		left -= chain->off - position;
		chain = chain->next;
		position = 0;
	}

	
	if (chain) {//设置evbuffer_chain内的偏移量
		pos->_internal.chain = chain;
		pos->_internal.pos_in_chain = position + left;
	} else {//跨过了所有的节点
		pos->_internal.chain = NULL;
		pos->pos = -1;
	}

	EVBUFFER_UNLOCK(buf);

	return chain != NULL ? 0 : -1;
}

        可以看到,该函数只考虑了向后面的chain移动定位指针,不能向。当然如果参数position小于0,并且移动时并不会跨越当前的chain,还是可以的。不过最好不要这样做。如果确实想移回头,那么可以考虑下面的操作。

pos.position -= 20;//移回头20个字节。
evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET);


查找一个字符:

        有下面代码的前一个函数是可以用来查找一个字符的,第二个函数则是获取对于位置的字符。
static inline ev_ssize_t
evbuffer_strchr(struct evbuffer_ptr *it, const char chr);

static inline char//获取对应位置的字符
evbuffer_getchr(struct evbuffer_ptr *it);

static inline int
evbuffer_strspn(struct evbuffer_ptr *ptr, const char *chrset);

        函数evbuffer_strchr是从it指向的evbuffer_chain开始查找,会往后面的链表查找。it是一个值-结果参数,如果查找到了,那么it将会指明被查找字符的位置,并返回相对于evbuffer的总偏移量(即it->pos)。如果没有找到,就返回-1。由于实现都是一般的字符比较,所以就不列出代码了。函数evbuffer_getchr很容易理解也不列出代码了。

 

        第三个函数evbuffer_strspn的参数chrset虽然是一个字符串,其实内部也是比较字符的。该函数所做的操作和C语言标准库里面的strspn函数是一样的。这里也不多说了。关于strspn函数的理解可以查看 这里


查找一个字符串:

        字符串的查找函数有evbuffer_search_range和evbuffer_search,后者调用前者完成查找。

        在讲查找前,先看一个字符串比较函数evbuffer_ptr_memcmp。该函数是比较某一个字符串和从evbuffer中某个位置开始的字符是否相等。明显比较的时候需要考虑到跨evbuffer_chain的问题。

static int //匹配成功会返回0
evbuffer_ptr_memcmp(const struct evbuffer *buf, const struct evbuffer_ptr *pos,
    const char *mem, size_t len)
{
	struct evbuffer_chain *chain;
	size_t position;
	int r;

	//链表数据不够
	if (pos->pos + len > buf->total_len)
		return -1;
	
	//需要考虑这个要匹配的字符串被分散在两个evbuffer_chain中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值