Linux下对libevent的evbuffer模块裁剪移植

1. 背景

libevent作为一款高效的网络开发库,内部模块也有许多优秀的实现。
evbuffer为libevent的核心缓冲器功能,提供了与I/O的操作的高效结合:数据拷贝、移动、读写。
上篇文章《Linux下使用gtest对接口进行单元测试》对evbuffer准备了单元测试,本节尝试将evbuffer模块单独裁剪出来,学习一下evbuffer的实现。

2. 源码分析

2.1 结构体分析

主要结构体为 struct evbufferstruct 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;
    ...
};

主要成员为firstlastlast_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挂载用户数据。

chain3
chain2
chain1
evbuffer
buffer
next
buffer
next
buffer
next
first
last
last_with_datap
null

关于evbuffer链表的设计的思路,evbuffer为何使用链表的形式实现,而不是使用单个地址的大堆来完成?

翻看了接口使用后,我理解是这几个方面考虑:

  1. 链表方便对数据节点进行添加、删除操作(类似链表与数组的区别);
  2. 两个evbuffer的数据移动,可以理解为两个链表的操作,避免内存拷贝;
  3. 链表为分散的内存地址,evbuffer配合iovec的思路,实现了writev、readv的功能;
  4. 在系统调用函数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.裁剪

我们主要工作是裁剪出核心功能进行学习,其他的高级功能裁剪功能如下:

  1. 回调函数,通过HAVE_CALLBACK注释掉
  2. 文件操作,EVBUFFER_FILESEGMENT、EVBUFFER_SENDFILE
  3. 只读属性,EVBUFFER_IMMUTABLE
  4. 多处引用,EVBUFFER_REFERENCE、EVBUFFER_MULTICAST
  5. 内存驻留,EVBUFFER_MEM_PINNED_R、EVBUFFER_MEM_PINNED_W
  6. 线程安全,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
#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值