目录列表
1. 背景
libevent作为一款高效的网络开发库,内部模块也有许多优秀的实现。
evbuffer为libevent的核心缓冲器功能,提供了与I/O的操作的高效结合:数据拷贝、移动、读写。
上篇文章《Linux下使用gtest对接口进行单元测试》对evbuffer准备了单元测试,本节尝试将evbuffer模块单独裁剪出来,学习一下evbuffer的实现。
2. 源码分析
2.1 结构体分析
主要结构体为 struct evbuffer
和 struct evbuffer_chain
两个数据结构,
通过链表的形式对碎片数据进行组织。
struct evbuffer {
/** The first chain in this buffer's linked list of chains. */
struct evbuffer_chain *first;
/** The last chain in this buffer's linked list of chains. */
struct evbuffer_chain *last;
/** Pointer to the next pointer pointing at the 'last_with_data' chain.
*
* To unpack:
*
* The last_with_data chain is the last chain that has any data in it.
* If all chains in the buffer are empty, it is the first chain.
* If the buffer has no chains, it is NULL.
*
* The last_with_datap pointer points at _whatever 'next' pointer_
* points at the last_with_datap chain. If the last_with_data chain
* is the first chain, or it is NULL, then the last_with_datap pointer
* is &buf->first.
*/
struct evbuffer_chain **last_with_datap;
/** Total amount of bytes stored in all chains.*/
size_t total_len;
...
};
主要成员为first
、last
、last_with_datap
这些指针,用于构建链表的处理,
另外,evbuffer还支持操作过程中添加、删除数据调用回调函数处理,本节对该功能进行裁剪。
/** A single item in an evbuffer. */
struct evbuffer_chain {
/** points to next buffer in the chain */
struct evbuffer_chain *next;
/** total allocation available in the buffer field. */
size_t buffer_len;
/** unused space at the beginning of buffer or an offset into a
* file for sendfile buffers. */
ssize_t misalign;
/** Offset into buffer + misalign at which to start writing.
* In other words, the total number of bytes actually stored
* in buffer. */
size_t off;
/** Set if special handling is required for this chain */
unsigned flags;
/** number of references to this chain */
int refcnt;
/** Usually points to the read-write memory belonging to this
* buffer allocated as part of the evbuffer_chain allocation.
* For mmap, this can be a read-only buffer and
* EVBUFFER_IMMUTABLE will be set in flags. For sendfile, it
* may point to NULL.
*/
unsigned char *buffer;
};
chain结构为单个节点的信息,主要维护了指针域数据,并且buffer挂载用户数据。
关于evbuffer链表的设计的思路,evbuffer为何使用链表的形式实现,而不是使用单个地址的大堆来完成?
翻看了接口使用后,我理解是这几个方面考虑:
- 链表方便对数据节点进行添加、删除操作(类似链表与数组的区别);
- 两个evbuffer的数据移动,可以理解为两个链表的操作,避免内存拷贝;
- 链表为分散的内存地址,evbuffer配合iovec的思路,实现了writev、readv的功能;
- 在系统调用函数read、write中,操作大块数据时(如10M),是通过多次调用完成的(如每次4KB,多次循环);
2.2 主要接口
接口名称 | 说明 |
---|---|
struct evbuffer *evbuffer_new(void); | 为新的evbuffer分配空间 |
void evbuffer_free(struct evbuffer *buf); | 对evbuffer释放空间 |
size_t evbuffer_get_length(const struct evbuffer *buf); | 获取evbuffer总长度 |
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); | 追加数据到evbuffer尾部 |
int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size); | 在evbuffer头部插入数据 |
unsigned char *evbuffer_pullup(struct evbuffer *buf, ssize_t size); | 使evbuffer开头的数据连续 |
int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen); | 读取并删除数据 |
ssize_t evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen); | 读取但保留数据 |
int evbuffer_drain(struct evbuffer *buf, size_t len); | 删除数据 |
3.裁剪
我们主要工作是裁剪出核心功能进行学习,其他的高级功能裁剪功能如下:
- 回调函数,通过HAVE_CALLBACK注释掉
- 文件操作,EVBUFFER_FILESEGMENT、EVBUFFER_SENDFILE
- 只读属性,EVBUFFER_IMMUTABLE
- 多处引用,EVBUFFER_REFERENCE、EVBUFFER_MULTICAST
- 内存驻留,EVBUFFER_MEM_PINNED_R、EVBUFFER_MEM_PINNED_W
- 线程安全,evbuffer可以支持线程安全的,我们暂时通过宏将
evthread-internal.h
替换掉
3.1 兼容性定义
对应evbuffer的属性,我们进行调整定义
/*
* Minimum allocation for a chain.
* We define this so that we're burning no
* more than 5% of each allocation on overhead.
* It would be nice to lose even less space, though.
*/
#define MIN_BUFFER_SIZE 1024
#define EVBUFFER_CHAIN_MAX (1024 * 1024)
替换掉libevent的内部数据结构(目前仅考虑Linux-x86_64平台)
#ifdef __cplusplus
# define __STDC_LIMIT_MACROS
# define __STDC_CONSTANT_MACROS
# include <stdint.h>
#endif
#define EV_UINT64_MAX UINT64_MAX
#define EV_INT64_MAX INT64_MAX
#define EV_INT64_MIN INT64_MIN
#define EV_UINT32_MAX UINT32_MAX
#define EV_INT32_MAX INT32_MAX
#define EV_INT32_MIN INT32_MIN
#define EV_UINT16_MAX UINT16_MAX
#define EV_INT16_MAX INT16_MAX
#define EV_INT16_MIN INT16_MIN
#define EV_UINT8_MAX UINT8_MAX
#define EV_INT8_MAX INT8_MAX
#define EV_INT8_MIN INT8_MIN
#define EV_SIZE_MAX EV_UINT64_MAX
#define EV_SSIZE_MAX EV_INT64_MAX
/*
* include/event2/util.h
*/
typedef unsigned char ev_uint8_t;
typedef unsigned short ev_uint16_t;
typedef unsigned int ev_uint32_t;
typedef unsigned long long ev_uint64_t;
#define ev_uintptr_t uintptr_t
#define ev_intptr_t intptr_t
typedef ssize_t ev_ssize_t;
typedef ev_int64_t ev_off_t;
替换掉libevent内存管理,更换为标准的系统调用
/*
* mm-internal.h
*/
#define mm_malloc(sz) malloc(sz)
#define mm_calloc(n, sz) calloc((n), (sz))
#define mm_strdup(s) strdup(s)
#define mm_realloc(p, sz) realloc((p), (sz))
#define mm_free(p) free(p)
/*
* evutil.c
*/
#define evutil_vsnprintf vsnprintf
#