1. 缓冲区的chunk的分配
在分配chunk时,其实chunk头包含一些附加信息,如chunk块存放数据的空间大小(buffer_len),起始地址(buffer),当前数据尾到起始片的偏移(off)等。chunk的大小算上头部信息,至少是1024个字节。

static struct evbuffer_chain *
evbuffer_chain_new(size_t size)  //这个size不包括chunk头大小
{struct evbuffer_chain *chain;size_t to_alloc;if (size > EVBUFFER_CHAIN_MAX - EVBUFFER_CHAIN_SIZE)   //如果分配空间的大小加上头部大小大于chunk的最大值就直接返回。return (NULL);size += EVBUFFER_CHAIN_SIZE; //加上chunk头大小/* get the next largest memory that can hold the buffer *///计算容纳size大小的最小值if (size < EVBUFFER_CHAIN_MAX / 2) {to_alloc = MIN_BUFFER_SIZE;while (to_alloc < size) {to_alloc <<= 1;}} else {to_alloc = size;}/* we get everything in one chunk */if ((chain = mm_malloc(to_alloc)) == NULL)return (NULL);//chunk头部作息初始化memset(chain, 0, EVBUFFER_CHAIN_SIZE);chain->buffer_len = to_alloc - EVBUFFER_CHAIN_SIZE;/* this way we can manipulate the buffer to different addresses,* which is required for mmap for example.*/chain->buffer = EVBUFFER_CHAIN_EXTRA(u_char, chain);return (chain);
}

2. 列表内容
因为缓冲区是通过evbuffer_chain的next指针连接起来的,这个是链表操作

static void
evbuffer_free_all_chains(struct evbuffer_chain *chain)
{struct evbuffer_chain *next;for (; chain; chain = next) {next = chain->next;evbuffer_chain_free(chain);}
}

3. 缓冲区中添加chunk
将chunk添加到evbuffer的尾部时,会释放掉所有空的chunk,在添加chunk时,不会调用回调

static void
evbuffer_chain_insert(struct evbuffer *buf,struct evbuffer_chain *chain)
{ASSERT_EVBUFFER_LOCKED(buf);if (*buf->last_with_datap == NULL) {  //表示原来的缓冲区不存在为空,不存在chunk时,将first和last指针赋值为chain/* 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 {//在添加时,会释放尾部所有的空chunkstruct 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 = &(*ch)->next;if (*ch == NULL) {/* There is no victim; just append this new chain. */buf->last->next = chain;if (chain->off)  //如果添加的chunk包含有用数据,就修改last_with_datap指针buf->last_with_datap = &buf->last->next;} else {/* Replace all victim chains with this chain. */EVUTIL_ASSERT(evbuffer_chains_all_empty(*ch));evbuffer_free_all_chains(*ch);*ch = chain;}buf->last = chain;}buf->total_len += chain->off;
}

4. chunk块中flag标志的设置及取消
evbuffer_chain中的flag标志主要是锁定读还是写两种情况,在设置取消操作时比较简单

//设置
_evbuffer_chain_pin(struct evbuffer_chain *chain, unsigned flag)
{EVUTIL_ASSERT((chain->flags & flag) == 0);chain->flags |= flag;
}
//取消
void
_evbuffer_chain_unpin(struct evbuffer_chain *chain, unsigned flag)
{EVUTIL_ASSERT((chain->flags & flag) != 0);chain->flags &= ~flag;if (chain->flags & EVBUFFER_DANGLING)evbuffer_chain_free(chain);
}

5. 缓冲区回调的触发
在写操作使用非阻塞模式时,在调用网络接口可写状态监听时,总是满足的,所以在写操作完成后,会将对应的fd事件从事件循环中删除,而当向缓冲区中写数据时,会将对应的事件添加到event_base中,这时,这个回调就显得比较重要了

void
evbuffer_invoke_callbacks(struct evbuffer *buffer)
{if (TAILQ_EMPTY(&buffer->callbacks)) {buffer->n_add_for_cb = buffer->n_del_for_cb = 0;return;}if (buffer->deferred_cbs) {if (buffer->deferred.queued)return;_evbuffer_incref_and_lock(buffer);if (buffer->parent)bufferevent_incref(buffer->parent);EVBUFFER_UNLOCK(buffer);event_deferred_cb_schedule(buffer->cb_queue, &buffer->deferred);  //将缓冲区回调添加加event_base中的回调队列中}evbuffer_run_callbacks(buffer, 0); //调用回调函数
}

6. 缓冲区大小的调节
在I/O上有数据可读时,将I/O上的数据读入到缓冲区,需要调节缓冲区的大小来填装从I/O上读到的数据。主要是函数evbuffer_reserve_space来完成。

int
evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size,struct evbuffer_iovec *vec, int n_vecs)
{struct evbuffer_chain *chain, **chainp;int n = -1;EVBUFFER_LOCK(buf);if (buf->freeze_end)  //尾部禁止写,退出goto done;if (n_vecs < 1)goto done;if (n_vecs == 1) {if ((chain = evbuffer_expand_singlechain(buf, size)) == NULL)goto done;vec[0].iov_base = CHAIN_SPACE_PTR(chain);vec[0].iov_len = (size_t) CHAIN_SPACE_LEN(chain);EVUTIL_ASSERT(size<0 || (size_t)vec[0].iov_len >= (size_t)size);n = 1;} else {if (_evbuffer_expand_fast(buf, size, n_vecs)<0)goto done;n = _evbuffer_read_setup_vecs(buf, size, vec, n_vecs,&chainp, 0);}done:EVBUFFER_UNLOCK(buf);return n;}

7. 设置读入的缓冲区
在集中读时,设置iovec的缓冲区

int
_evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,struct evbuffer_iovec *vecs, int n_vecs_avail,struct evbuffer_chain ***chainp, int exact)
{struct evbuffer_chain *chain;struct evbuffer_chain **firstchainp;size_t so_far;int i;ASSERT_EVBUFFER_LOCKED(buf);if (howmuch < 0)return -1;so_far = 0;/* Let firstchain be the first chain with any space on it */firstchainp = buf->last_with_datap;if (CHAIN_SPACE_LEN(*firstchainp) == 0) {firstchainp = &(*firstchainp)->next;}chain = *firstchainp;for (i = 0; i < n_vecs_avail && so_far < (size_t)howmuch; ++i) {size_t avail = (size_t) CHAIN_SPACE_LEN(chain);if (avail > (howmuch - so_far) && exact)avail = howmuch - so_far;vecs[i].iov_base = CHAIN_SPACE_PTR(chain);vecs[i].iov_len = avail;so_far += avail;chain = chain->next;}*chainp = firstchainp;return i;
}

8. 读入缓冲区
在I/O上有数据可读时就将数据读入evbuffer中,函数evbuffer_read来完成,同时设置缓冲区的回调(即应用层的处理)

int
evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
{struct evbuffer_chain **chainp;int n;int result;#ifdef USE_IOVEC_IMPLint nvecs, i, remaining;
#elsestruct evbuffer_chain *chain;unsigned char *p;
#endifEVBUFFER_LOCK(buf);if (buf->freeze_end) {result = -1;goto done;}n = get_n_bytes_readable_on_socket(fd);if (n <= 0 || n > EVBUFFER_MAX_READ)n = EVBUFFER_MAX_READ;if (howmuch < 0 || howmuch > n)howmuch = n;#ifdef USE_IOVEC_IMPL/* Since we can use iovecs, we're willing to use the last* NUM_READ_IOVEC chains. */if (_evbuffer_expand_fast(buf, howmuch, NUM_READ_IOVEC) == -1) {result = -1;goto done;} else {IOV_TYPE vecs[NUM_READ_IOVEC];
#ifdef _EVBUFFER_IOVEC_IS_NATIVEnvecs = _evbuffer_read_setup_vecs(buf, howmuch, vecs,NUM_READ_IOVEC, &chainp, 1);
#else/* We aren't using the native struct iovec.  Therefore,we are on win32. */struct evbuffer_iovec ev_vecs[NUM_READ_IOVEC];nvecs = _evbuffer_read_setup_vecs(buf, howmuch, ev_vecs, 2,&chainp, 1);for (i=0; i < nvecs; ++i)WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]);
#endif#ifdef WIN32{DWORD bytesRead;DWORD flags=0;if (WSARecv(fd, vecs, nvecs, &bytesRead, &flags, NULL, NULL)) {/* The read failed. It might be a close,* or it might be an error. */if (WSAGetLastError() == WSAECONNABORTED)n = 0;elsen = -1;} elsen = bytesRead;}
#elsen = readv(fd, vecs, nvecs);
#endif}#else /*!USE_IOVEC_IMPL*//* If we don't have FIONREAD, we might waste some space here *//* XXX we _will_ waste some space here if there is any space left* over on buf->last. */if ((chain = evbuffer_expand_singlechain(buf, howmuch)) == NULL) {result = -1;goto done;}/* We can append new data at this point */p = chain->buffer + chain->misalign + chain->off;#ifndef WIN32n = read(fd, p, howmuch);
#elsen = recv(fd, p, howmuch, 0);
#endif
#endif /* USE_IOVEC_IMPL */if (n == -1) {result = -1;goto done;}if (n == 0) {result = 0;goto done;}#ifdef USE_IOVEC_IMPLremaining = n;for (i=0; i < nvecs; ++i) {/* can't overflow, since only mutable chains have* huge misaligns. */size_t space = (size_t) CHAIN_SPACE_LEN(*chainp);/* XXXX This is a kludge that can waste space in perverse* situations. */if (space > EVBUFFER_CHAIN_MAX)space = EVBUFFER_CHAIN_MAX;if ((ev_ssize_t)space < remaining) {(*chainp)->off += space;remaining -= (int)space;} else {(*chainp)->off += remaining;buf->last_with_datap = chainp;break;}chainp = &(*chainp)->next;}
#elsechain->off += n;advance_last_with_data(buf);
#endifbuf->total_len += n;buf->n_add_for_cb += n;/* Tell someone about changes in this buffer */evbuffer_invoke_callbacks(buf);result = n;
done:EVBUFFER_UNLOCK(buf);return result;
}

9. 缓冲区中的写操作
将缓冲区中的数据写入I/O,在写时分为集中写与一般的写。在一般写的情况下,如果要写的字节数分散在多个chunk中,这就要将多个chunk中的数据合并到一个chunk中,而将其它的释放,这种处理是通过函数evbuffer_pullup来完成。将缓冲区的数据写到socket是通过evbuffer_write_atmost来完成。在写完后,会将缓冲区中的一部分chunk释放。

int
evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,ev_ssize_t howmuch)
{int n = -1;EVBUFFER_LOCK(buffer);if (buffer->freeze_start) {goto done;}if (howmuch < 0 || (size_t)howmuch > buffer->total_len)howmuch = buffer->total_len;if (howmuch > 0) {
#ifdef USE_SENDFILEstruct evbuffer_chain *chain = buffer->first;if (chain != NULL && (chain->flags & EVBUFFER_SENDFILE))n = evbuffer_write_sendfile(buffer, fd, howmuch);else {
#endif
#ifdef USE_IOVEC_IMPLn = evbuffer_write_iovec(buffer, fd, howmuch);   //集中写
#elif defined(WIN32)                                //一般写/* XXX(nickm) Don't disable this code until we know if* the WSARecv code above works. */void *p = evbuffer_pullup(buffer, howmuch);EVUTIL_ASSERT(p || !howmuch);n = send(fd, p, howmuch, 0);
#elsevoid *p = evbuffer_pullup(buffer, howmuch);EVUTIL_ASSERT(p || !howmuch);n = write(fd, p, howmuch);                  //一般写
#endif
#ifdef USE_SENDFILE}
#endif}if (n > 0)evbuffer_drain(buffer, n);   //释放done:EVBUFFER_UNLOCK(buffer);return (n);
}

libevent中的缓冲区(二)相关推荐

  1. libevent中的缓冲区(一)

     libevent中的缓冲区定义为evbuffer,主要在文件evbuffer-internal.h文件中,定义如下 struct evbuffer {/** The first chain in ...

  2. 在libevent中使用线程池

    一 线程的初始化 1线程对象 在进行事件驱动时,每个线程需建立自己的事件根基.由于libevent未提供线程之间通信的方式,我们采用管道来进行线程的通信.同时为方便主线程分配线程,我们还需保留各个线程 ...

  3. libevent中的bufferevent

    bufferevent是libevent中处理网络事件很重要也是比较复杂的一个模块,其中包含一个读事件,一个写事件,两个缓冲区(读和写).读写水位.三个回调(读.写.出错)和函数指针组成的操作集.目前 ...

  4. java nio 缓冲区(二)

    本文章来自于本人个人博客:java nio 缓冲区(二) 一,创建缓冲区 1.缓冲区的创建有两种方式,分别是ByteBuffer.allocate([int])或者ByteBuffer.wrap(by ...

  5. Java IO在Android中应用(二):APK加固

    Java I/O在Android中应用(二):APK加固套壳 前言(废话) 我,有两把键盘,第一把是Poker III(黑轴),第二把是Poker II(红轴).工作的时候我常用的是红轴的Poker ...

  6. libevent中的基本数据结构

     libevent中文件queue.h文件包含5种数据结构:单链表,双向链表,队列,尾队列,环形队列.在处理I/O和signal中的事件时,用的就是尾队列,下面就介绍这几种数据结构 1.单链表 链表 ...

  7. libevent中事件的添加与删除

     前面介绍了libevent中的hash表,在添加事件时,具体是如何操作的呢?事件操作主要是在evmap.c文件中,包含了io事件,signal事件的操作.在事件操作时,分两种情况,一种是利用ha ...

  8. libevent中的信号处理

    libevent中将信号集成到event_base_loop事件循环中,通过socketpair转换成I/O事件,本文主要介绍相关的转换. 1.将信号转成I/O 采用socket pair方式,一个s ...

  9. libevent中的时间及相关的管理

    libevent中的时间及相关的管理 在介绍时间之前,先说明几个与时间相关的函数及其用法 1.基础 1.1 clock_gettime(精度比较高,ns级) #include <time.h&g ...

最新文章

  1. 能让你纵享丝滑的SSR技术,转转这样实践
  2. 反沙箱——SetErrorMode
  3. 更新SQL Server实例所有数据库表统计信息
  4. 【BZOJ2751】【codevs1853】容易题,快速幂+逆元
  5. 算法设计与分析练习题答案
  6. 小白功能测试项目实战篇01-dtsshop开源商城项目之【会员管理】模块测试点分析
  7. 在服务器上搭建ftp站点
  8. 【虚拟机】虚拟机vmware设置全屏(直接设置)
  9. 使用DNSLog进行盲打
  10. 电脑重启f12怎么处理_联想电脑开机按f12后,怎么设置默认启动项
  11. Eclipse开发Android的安装配置。
  12. await把Promise解析为普通对象,async函数return的返回值是promise对象,await后转化为普通Object
  13. 王者s19服务器维护,王者S19丨4个必须知道的调整!最后一个不知没法玩!
  14. JAVA技术栈学习路线整理【java后端开发应聘向】
  15. 新edge浏览器快捷键
  16. .NET技术网站群系统介绍
  17. android studio 启动flutter项目报错
  18. KDJ日周月金叉共振指标
  19. 网站简约卡通风格404页面html源码下载
  20. Java开发者应该会哪些东西才不会被公司淘汰

热门文章

  1. Linux 调度器发展简述
  2. PostMessage()和SendMessage()
  3. python初学者web还是爬虫-还在纠结学爬虫还是数据分析,不如看看这篇文章
  4. python快速编程入门教程-半小时带你快速入门Python编程,Python快速入门教程
  5. python视频课程推荐-听说程序员都在用,5款Python开发工具推荐
  6. python turtle库画图案-Python如何使用turtle库绘制图形
  7. 想学python从哪里入手-想要学习python,如何入手学习?
  8. 学python可以考证吗-python能考证么
  9. python语言入门m-Python语言入门详解!快速学成Python!
  10. python 文字语音朗读-教你用 Python 来朗读网页