libevent(七)evbuffer

通常这些函数我们不会自己去调用他们,首先一般使用到它们是在bufferevent中,即在调用bufferevent_socket_new时会为input和output的bufferevent分配内存,并封装一系列的接口供用户调用,而接口的底层都是对evbuffer的操作,现在我们从struct evbuffer结构体开始:

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;
    /**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_
     * pointing at the last_with_data 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;


    /** Number of bytes we have added to the buffer since we last tried to
     * invoke callbacks. */
    size_t n_add_for_cb;
    /** Number of bytes we have removed from the buffer since we last
     * tried to invoke callbacks. */
    size_t n_del_for_cb;


#ifndef EVENT__DISABLE_THREAD_SUPPORT
    /** A lock used to mediate access to this buffer. */
    void *lock;
#endif
    /** True iff we should free the lock field when we free this
     * evbuffer. */
    unsigned own_lock : 1;
    /** True iff we should not allow changes to the front of the buffer
     * (drains or prepends). */
    unsigned freeze_start : 1;
    /** True iff we should not allow changes to the end of the buffer
     * (appends) */
    unsigned freeze_end : 1;
    /** True iff this evbuffer's callbacks are not invoked immediately
     * upon a change in the buffer, but instead are deferred to be invoked
     * from the event_base's loop.  Useful for preventing enormous stack
     * overflows when we have mutually recursive callbacks, and for
     * serializing callbacks in a single thread. */
    unsigned deferred_cbs : 1;
#ifdef _WIN32
    /** True iff this buffer is set up for overlapped IO. */
    unsigned is_overlapped : 1;
#endif
    /** Zero or more EVBUFFER_FLAG_* bits */
    ev_uint32_t flags;


    /** Used to implement deferred callbacks. */
    struct event_base *cb_queue;


    /** A reference count on this evbuffer.  When the reference count
     * reaches 0, the buffer is destroyed.  Manipulated with
     * evbuffer_incref and evbuffer_decref_and_unlock and
     * evbuffer_free. */
    int refcnt;


    /** A struct event_callback handle to make all of this buffer's callbacks
     * invoked from the event loop. */
    struct event_callback deferred;


    /** A doubly-linked-list of callback functions */
    LIST_HEAD(evbuffer_cb_queue, evbuffer_cb_entry) callbacks;


    /** The parent bufferevent object this evbuffer belongs to.
     * NULL if the evbuffer stands alone. */
    struct bufferevent *parent;
};

struct evbuffer_chain

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. */
    ev_misalign_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;
#define EVBUFFER_FILESEGMENT    0x0001  /**< A chain used for a file segment */
#define EVBUFFER_SENDFILE   0x0002  /**< a chain used with sendfile */
#define EVBUFFER_REFERENCE  0x0004  /**< a chain with a mem reference */
#define EVBUFFER_IMMUTABLE  0x0008  /**< read-only chain */
    /** a chain that mustn't be reallocated or freed, or have its contents
     * memmoved, until the chain is un-pinned. */
#define EVBUFFER_MEM_PINNED_R   0x0010
#define EVBUFFER_MEM_PINNED_W   0x0020
#define EVBUFFER_MEM_PINNED_ANY (EVBUFFER_MEM_PINNED_R|EVBUFFER_MEM_PINNED_W)
    /** a chain that should be freed, but can't be freed until it is
     * un-pinned. */
#define EVBUFFER_DANGLING   0x0040
    /** a chain that is a referenced copy of another chain */
#define EVBUFFER_MULTICAST  0x0080


    /** 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;
};

evbuffer结构体在bufferevent中是用来存储数据的,这些数据存放在evbuffer的一条链表中,每个链表成员都是一个evbufer_chain结构。
图示如下:
libevent(七)evbuffer_第1张图片
创建一个evbuffer结构,在bufferevent_socket_new中函数bufferevent_init_common_会分配两个evuffer分别作为输入和输出缓冲区。

struct evbuffer *
evbuffer_new(void)
{
    struct evbuffer *buffer;

    buffer = mm_calloc(1, sizeof(struct evbuffer));//为evbuffer分配内存
    if (buffer == NULL)
        return (NULL);


    LIST_INIT(&buffer->callbacks);//初始化回调函数队列
    buffer->refcnt = 1;
    buffer->last_with_datap = &buffer->first;


    return (buffer);
}

函数evbuffer_chain_new用于创建一个数据块(就这么称呼吧)

static struct evbuffer_chain *evbuffer_chain_new(size_t size)
{
    struct evbuffer_chain *chain;
    size_t to_alloc;


    if (size > EVBUFFER_CHAIN_MAX - EVBUFFER_CHAIN_SIZE)
        return (NULL);//限制了最大的evbuffer_chain的数据存放区大小,这个大小是((size_t)(9223372036854775807L)) - sizeof(struct evbuffer_chain))


    size += EVBUFFER_CHAIN_SIZE;//此时size为evbuffer_chain结构和数据存放区大小的总和


    /* get the next largest memory that can hold the buffer */
    if (size < EVBUFFER_CHAIN_MAX / 2) {
        to_alloc = MIN_BUFFER_SIZE;
        while (to_alloc < size) {
            to_alloc <<= 1;
        }//如果size小于最大内存块的一半,则以内存块最小值开始翻倍,直到内存块值大于size
    } else {
        to_alloc = size;//如果size大于最大内存块的一半,就直接分配size大小的内存
    }


    /* we get everything in one chunk */
    if ((chain = mm_malloc(to_alloc)) == NULL)
        return (NULL);//内存分配


    memset(chain, 0, EVBUFFER_CHAIN_SIZE);。。//清0除数据块之外的内存


    chain->buffer_len = to_alloc - EVBUFFER_CHAIN_SIZE;//此时buffer_len记录为数据块的大小


    /* this way we can manipulate the buffer to different addresses,
     * which is required for mmap for example.
     */
    chain->buffer = EVBUFFER_CHAIN_EXTRA(unsigned char, chain);//chain->buffer = (unsigned char *)((struct evbuffer_chain *)(chain) + 1);指向此struct evbuffer_chain的尾地址的下一个字节


    chain->refcnt = 1;


    return (chain);
}

函数evbuffer_chain_insert,用于插入数据块到evbuffer

static void
evbuffer_chain_insert(struct evbuffer *buf,
    struct evbuffer_chain *chain)
{
    ASSERT_EVBUFFER_LOCKED(buf);
    if (*buf->last_with_datap == NULL) {
        /* There are no chains data on the buffer at all. */
        EVUTIL_ASSERT(buf->last_with_datap == &buf->first);
        EVUTIL_ASSERT(buf->first == NULL);
        buf->first = buf->last = chain;//第一次插入数据,首尾指针都应该指向此内存块

    } else {
        struct evbuffer_chain **chp;
        chp = evbuffer_free_trailing_empty_chains(buf);
        static struct evbuffer_chain **
        evbuffer_free_trailing_empty_chains(struct evbuffer *buf)
        {
            struct evbuffer_chain **ch = buf->last_with_datap;
            /* Find the first victim chain.  It might be *last_with_datap */
            while ((*ch) && ((*ch)->off != 0 || CHAIN_PINNED(*ch)))
//(*ch)->off != 0表示该evbuffer_chain有数据了,CHAIN_PINNED(*ch)则表示该evbuffer_chain不能被修改,在链表中寻找到一个可以使用的evbuffer_chain,可以使用是指该chain没有数据并且可以修改
                ch = &(*ch)->next;//直到找到一个这样的内存块
            if (*ch) {//evbuffer_chain始终是指向最后一个有数据的buffer_chain,如果后面有空的数据块,就都释放掉
                EVUTIL_ASSERT(evbuffer_chains_all_empty(*ch));
                evbuffer_free_all_chains(*ch);
                *ch = NULL;
            }
            return ch;
        }
        *chp = chain;
        if (chain->off)
            buf->last_with_datap = chp;//指向传如的chain的地址,,如果此chain的off不为0,即里面有数据,则让buf->last_with_datap指向它
        buf->last = chain;
    }
    buf->total_len += chain->off;
}

向链表尾部添加数据,函数evbuffer_add

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;
    }/冻结缓冲区尾部,禁止诗句追加
    /* Prevent buf->total_len overflow */
    if (datlen > EV_SIZE_MAX - buf->total_len) {
        goto done;
    }//数据长度大于了最大长度减去buf中已经存放数据的长度,EV_SIZE_MAX值为18446744073709551615UL


    if (*buf->last_with_datap == NULL) {
        chain = buf->last;//第一次插入数据
    } else {
        chain = *buf->last_with_datap;
    }//如果不是第一次插入数据,chain指向最后一和存放有数据的evbuffer_chain


    /* If there are no chains allocated for this buffer, allocate one
     * big enough to hold all the data. */
    if (chain == NULL) {
        chain = evbuffer_chain_new(datlen);
        if (!chain)
            goto done;
        evbuffer_chain_insert(buf, chain);
    }//chain为NULL表明第一次插入数据,分配一个能hold住这些数据的内存块


    if ((chain->flags & EVBUFFER_IMMUTABLE) == 0) {//如果最后一个chain不是一个read-only的chain
        /* Always true for mutable buffers */
        EVUTIL_ASSERT(chain->misalign >= 0 &&
            (ev_uint64_t)chain->misalign <= EVBUFFER_CHAIN_MAX);
        remain = chain->buffer_len - (size_t)chain->misalign - chain->off;//最后一个chain有剩余数据块空间
        if (remain >= datlen) {//剩余空间可以存放下数据
            /* there's enough space to hold all the data in the
             * current last chain */
            memcpy(chain->buffer + chain->misalign + chain->off,
                data, datlen);
            chain->off += datlen;
            buf->total_len += datlen;
            buf->n_add_for_cb += datlen;
            goto out;
        } else if (!CHAIN_PINNED(chain) &&
            evbuffer_chain_should_realign(chain, datlen)) {//该chain可修改,且通过调整后也可以放下此次数据。这里调整的意思应该是这个chain的中本来有数据,但是数据并不在数据块的首地址,即数据块前面是有空余的,将chain中的原始数据移动到数据块首地址后,剩余空间可以放下此次数据
            /* we can fit the data into the misalignment */
            evbuffer_chain_align(chain);//调整数据


            memcpy(chain->buffer + chain->off, data, datlen);
            chain->off += datlen;
            buf->total_len += datlen;
            buf->n_add_for_cb += datlen;
            goto out;
        }
    } else {
        /* we cannot write any data to the last chain */
        remain = 0;//这个数据块不能再写入数据了,即本来chian链表中最后一个数据块有数据,但是数据刚好写满,或者此数据块中数据不满,但是调整后也不能容纳此次数据,就该分配新的数据块了
    }
    to_alloc = chain->buffer_len;
    if (to_alloc <= EVBUFFER_CHAIN_MAX_AUTO_SIZE/2)
        to_alloc <<= 1;
    if (datlen > to_alloc)
        to_alloc = datlen;
    tmp = evbuffer_chain_new(to_alloc);//分配一个chain内存块
    if (tmp == NULL)
        goto done;


    if (remain) {
        memcpy(chain->buffer + chain->misalign + chain->off,
            data, remain);
        chain->off += remain;
        buf->total_len += remain;
        buf->n_add_for_cb += remain;
    }//前一个数据块中数据不满,但是调整后也不能容纳此次数据,先将前一个数据块写满


    data += remain;
    datlen -= remain;


    memcpy(tmp->buffer, data, datlen);//将多于数据放入到新分配的内存块
    tmp->off = datlen;
    evbuffer_chain_insert(buf, tmp);/插入此chain
    buf->n_add_for_cb += datlen;//记录添加了多少数据


out:
    evbuffer_invoke_callbacks_(buf);//这个函数待会分析
    result = 0;
done:
    EVBUFFER_UNLOCK(buf);
    return result;
}

同样还有操在链表头部添加数据,从中复制数据的操作

现在看看从evbuffer中删除数据:

int
evbuffer_drain(struct evbuffer *buf, size_t len)
{
    struct evbuffer_chain *chain, *next;
    size_t remaining, old_len;
    int result = 0;


    EVBUFFER_LOCK(buf);
    old_len = buf->total_len;


    if (old_len == 0)
        goto done;


    if (buf->freeze_start) {
        result = -1;
        goto done;
    }//冻结数据首部,防止添加数据到evbuffer的chain链首部


    if (len >= old_len && !HAS_PINNED_R(buf)) {//要删除的数据量大于等于已有的数据量。并且这个evbuffer是可以删除的
        len = old_len;
        for (chain = buf->first; chain != NULL; chain = next) {
            next = chain->next;
            evbuffer_chain_free(chain);//删除所有数据块
        }


        ZERO_CHAIN(buf);//重新初始化evbuffer_chain链表
    } else {//要删除的数据量小于已有的数据量
        if (len >= old_len)
            len = old_len;


        buf->total_len -= len;
        remaining = len;
        for (chain = buf->first;
             remaining >= chain->off;
             chain = next) {
            next = chain->next;
            remaining -= chain->off;


            if (chain == *buf->last_with_datap) {//如果已经删除到最后一个数据块
                buf->last_with_datap = &buf->first;
            }
            if (&chain->next == buf->last_with_datap)//删除到倒数第二个有数据的evbuffer_chain
                buf->last_with_datap = &buf->first;


            if (CHAIN_PINNED_R(chain)) {//这个chain被固定了,不能删除
                EVUTIL_ASSERT(remaining == 0);
                chain->misalign += chain->off;
                chain->off = 0;
                break;//后面的evbuffer_chain也是固定的
            } else
                evbuffer_chain_free(chain);//直接删除chain
        }
        //到这里,表明这个chain中只能删除部分数据

        buf->first = chain;
        EVUTIL_ASSERT(remaining <= chain->off);
        chain->misalign += remaining;//清除掉应该删除的数据,这个域存放没有被删除的数据的第一个字节的数组下标
        chain->off -= remaining;//删除数据,记录偏移量,此处偏移量已经不是从数据块首部开始了,而是从misalign开始
    }


    buf->n_del_for_cb += len;//记录被删除了多少数据
    /* Tell someone about changes in this buffer */
    evbuffer_invoke_callbacks_(buf);//这个待会看


done:
    EVBUFFER_UNLOCK(buf);
    return result;
}

我们一般不会直接删除数据,而是获取这些数据再从数据块删除它们,也就是先复制这些数据到另一个地方,再删除这些数据。

通常我们会调用函数evbuffer_remove

/* Reads data from an event buffer and drains the bytes read */
int
evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
{
    ev_ssize_t n;
    EVBUFFER_LOCK(buf);
    n = evbuffer_copyout_from(buf, NULL, data_out, datlen);//复制原理很简单,memcpy
    if (n > 0) {
        if (evbuffer_drain(buf, n)<0)//删除
            n = -1;
    }
    EVBUFFER_UNLOCK(buf);
    return (int)n;
}

至此一般我们添加数据会调用到函数evbuffer_add,删除或说移除数据调用函数evbuffer_remove

现在看看evbuffer的回调函数

evbuffer中还有一个很重要的成员:LIST_HEAD(evbuffer_cb_queue, evbuffer_cb_entry) callbacks;回调函数链表。

struct evbuffer_cb_info {
    /** The number of bytes in this evbuffer when callbacks were last
     * invoked. */
    size_t orig_size;//添加或者删除数据之前的evbuffer有多少字节的数据
    /** The number of bytes added since callbacks were last invoked. */
    size_t n_added;//添加了多少数据
    /** The number of bytes removed since callbacks were last invoked. */
    size_t n_deleted;//删除了多少数据
    因为每次删除或者添加数据都会调用回调函数,所以上面的三个成员只能记录从上一次

    回调函数被调用后,到本次回调函数被调用这段时间的情况

};

struct evbuffer_cb_entry {
    /** Structures to implement a doubly-linked queue of callbacks */
    LIST_ENTRY(evbuffer_cb_entry) next;
    /** The callback function to invoke when this callback is called.
        If EVBUFFER_CB_OBSOLETE is set in flags, the cb_obsolete field is
        valid; otherwise, cb_func is valid. */
    union {
        evbuffer_cb_func cb_func;
        evbuffer_cb cb_obsolete;
    } cb;
    /** Argument to pass to cb. */
    void *cbarg;
    /** Currently set flags on this callback. */
    ev_uint32_t flags;
};
typedef void (*evbuffer_cb_func)(struct evbuffer *buffer, const struct evbuffer_cb_info *
info, void *arg);
typedef void (*evbuffer_cb)(struct evbuffer *buffer, size_t old_len, size_t new_len,
void *arg);

设置回调函数:

struct evbuffer_cb_entry *
evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg)
{
    struct evbuffer_cb_entry *e;
    if (! (e = mm_calloc(1, sizeof(struct evbuffer_cb_entry))))
        return NULL;
    EVBUFFER_LOCK(buffer);
    e->cb.cb_func = cb;
    e->cbarg = cbarg;
    e->flags = EVBUFFER_CB_ENABLED;
    LIST_INSERT_HEAD(&buffer->callbacks, e, next);//插入到回调函数链表
    EVBUFFER_UNLOCK(buffer);
    return e;
}

如何调用回调函数

static void
evbuffer_run_callbacks(struct evbuffer *buffer, int running_deferred)
{
    struct evbuffer_cb_entry *cbent, *next;
    struct evbuffer_cb_info info;
    size_t new_size;
    ev_uint32_t mask, masked_val;
    int clear = 1;


    if (running_deferred) {
        mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
        masked_val = EVBUFFER_CB_ENABLED;
    } else if (buffer->deferred_cbs) {
        mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
        masked_val = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
        /* Don't zero-out n_add/n_del, since the deferred callbacks
           will want to see them. */
        clear = 0;
    } else {
        mask = EVBUFFER_CB_ENABLED;
        masked_val = EVBUFFER_CB_ENABLED;
    }


    ASSERT_EVBUFFER_LOCKED(buffer);


    if (LIST_EMPTY(&buffer->callbacks)) {
        buffer->n_add_for_cb = buffer->n_del_for_cb = 0;
        return;
    }
    if (buffer->n_add_for_cb == 0 && buffer->n_del_for_cb == 0)
        return;


    new_size = buffer->total_len;
    info.orig_size = new_size + buffer->n_del_for_cb - buffer->n_add_for_cb;
    info.n_added = buffer->n_add_for_cb;
    info.n_deleted = buffer->n_del_for_cb;
    if (clear) {
        buffer->n_add_for_cb = 0;
        buffer->n_del_for_cb = 0;
    }
    for (cbent = LIST_FIRST(&buffer->callbacks);
         cbent != LIST_END(&buffer->callbacks);
         cbent = next) {
        /* Get the 'next' pointer now in case this callback decides
         * to remove itself or something. */
        next = LIST_NEXT(cbent, next);


        if ((cbent->flags & mask) != masked_val)
            continue;


        if ((cbent->flags & EVBUFFER_CB_OBSOLETE))
            cbent->cb.cb_obsolete(buffer,
                info.orig_size, new_size, cbent->cbarg);
        else
            cbent->cb.cb_func(buffer, &info, cbent->cbarg);//遍历回调函数队列,调用回调函数
    }
}

static void
evbuffer_run_callbacks(struct evbuffer *buffer, int running_deferred)
{
struct evbuffer_cb_entry *cbent, *next;
struct evbuffer_cb_info info;
size_t new_size;
ev_uint32_t mask, masked_val;
int clear = 1;

if (running_deferred) {
    mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
    masked_val = EVBUFFER_CB_ENABLED;
} else if (buffer->deferred_cbs) {
    mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
    masked_val = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
    /* Don't zero-out n_add/n_del, since the deferred callbacks
       will want to see them. */
    clear = 0;
} else {
    mask = EVBUFFER_CB_ENABLED;
    masked_val = EVBUFFER_CB_ENABLED;
}


ASSERT_EVBUFFER_LOCKED(buffer);


if (LIST_EMPTY(&buffer->callbacks)) {
    buffer->n_add_for_cb = buffer->n_del_for_cb = 0;
    return;
}
if (buffer->n_add_for_cb == 0 && buffer->n_del_for_cb == 0)
    return;


new_size = buffer->total_len;
info.orig_size = new_size + buffer->n_del_for_cb - buffer->n_add_for_cb;
info.n_added = buffer->n_add_for_cb;
info.n_deleted = buffer->n_del_for_cb;
if (clear) {
    buffer->n_add_for_cb = 0;
    buffer->n_del_for_cb = 0;
}
for (cbent = LIST_FIRST(&buffer->callbacks);
     cbent != LIST_END(&buffer->callbacks);
     cbent = next) {
    /* Get the 'next' pointer now in case this callback decides
     * to remove itself or something. */
    next = LIST_NEXT(cbent, next);


    if ((cbent->flags & mask) != masked_val)
        continue;


    if ((cbent->flags & EVBUFFER_CB_OBSOLETE))
        cbent->cb.cb_obsolete(buffer,
            info.orig_size, new_size, cbent->cbarg);
    else
        cbent->cb.cb_func(buffer, &info, cbent->cbarg);//遍历回调函数队列,调用回调函数
}

}

在看到前面的 evbuffer_invoke_callbacks_函数

void
evbuffer_invoke_callbacks_(struct evbuffer *buffer)
{
    if (LIST_EMPTY(&buffer->callbacks)) {
        buffer->n_add_for_cb = buffer->n_del_for_cb = 0;
        return;
    }


    if (buffer->deferred_cbs) {
        if (event_deferred_cb_schedule_(buffer->cb_queue, &buffer->deferred)) {
            evbuffer_incref_and_lock_(buffer);
            if (buffer->parent)
                bufferevent_incref_(buffer->parent);
            EVBUFFER_UNLOCK(buffer);
        }
    }


    evbuffer_run_callbacks(buffer, 0);
}

即每次添加数据到evbuffer或从evbuffer删除数据,都会调用此函数

你可能感兴趣的:(libevent学习)