很多时候为了应对数据IO的“慢“或者其他原因都需要使用数据缓冲区。对于数据缓冲,我们不陌生,但是对于如何实现这个缓冲区,相信很多时候大家都没有考虑过。今天就通过分析libevent的buffer.c源码,看看libevent是如何实现这个缓冲区的。
数据缓冲区buffer是libevent中网络IO操作中最先接触数据的容器。
1. 缓冲区evbuffer结构
1 struct evbuffer { 2 //存放数据起始位置 3 u_char *buffer; 4 5 //buffer起始地址 6 u_char *orig_buffer; 7 8 //buffer起始地址与数据存放地址的偏移 9 size_t misalign; 10 11 //总共buffer的长度 12 size_t totallen; 13 14 //缓冲区数据长度 15 size_t off; 16 17 //回调函数 18 void (*cb)(struct evbuffer *, size_t, size_t, void *); 19 20 //回调需要的参数 21 void *cbarg; 22 };
2. evbuffer结构图
3. ebuffer如何变化
4. 重要的几个函数注释
1.evbuffer_add
1 //从data地址开始datlen个字节数据到evbuffer中 2 int 3 evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen) 4 { 5 // 整个buffer 6 //| totallen | 7 //|--------------|-----------|---------------------------|--------| 8 //|misalign(偏移) |off(数据区) |datlen(需要加入的数据长度) |剩余空间 | 9 // 10 size_t need = buf->misalign + buf->off + datlen; 11 size_t oldoff = buf->off; 12 13 //如果need大于了总长度,需要调整扩大 14 if (buf->totallen < need) { 15 //evbuffer调整扩大 16 if (evbuffer_expand(buf, datlen) == -1) 17 return (-1); 18 } 19 //将datlen长度的data数据复制到buffer中。 20 memcpy(buf->buffer + buf->off, data, datlen); 21 //复制成功,数据长度增加 22 buf->off += datlen; 23 //datlen不为0且buf有回调函数,调用回调函数,告知缓存变化 24 if (datlen && buf->cb != NULL) 25 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); 26 27 return (0); 28 }
2.evbuffer_drain
1 //drain:使流出;排掉水 2 //从缓冲区中读出len长度数据。 3 void 4 evbuffer_drain(struct evbuffer *buf, size_t len) 5 { 6 //记录当前缓冲区中的数据长度 7 size_t oldoff = buf->off; 8 9 //如果要读出的长度大于数据长度,就读出全部数据 10 if (len >= buf->off) { 11 12 // 整个buffer 13 //| totallen | 14 //|||-------------------------------------------------------------| 15 //|||剩余空间 | 16 // 17 18 //元素个数清零 19 buf->off = 0; 20 //数据缓冲地址前移到最前面的buf起始位置 21 buf->buffer = buf->orig_buffer; 22 //数据偏移置0 23 buf->misalign = 0; 24 goto done; 25 } 26 //如果读出数据不是全部数据 27 28 // 整个buffer 29 //| totallen | 30 //|--------------|-----------|------------------------------------| 31 //| misalign |off(数据区) | 剩余空间 | 32 // | | 33 // / 34 35 // 整个buffer 36 //| totallen | 37 //|-----------------|--------|------------------------------------| 38 //| misalign |off | 剩余空间 | 39 // 40 41 //buffer地址前移len 42 buf->buffer += len; 43 //misalign偏移加len 44 buf->misalign += len; 45 //由于读出数据,off减少len个数据 46 buf->off -= len; 47 48 done: 49 //缓冲区数据长度改变,调用回调函数 50 /* Tell someone about changes in this buffer */ 51 if (buf->off != oldoff && buf->cb != NULL) 52 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); 53 54 }
3.evbuffer_align
1 //buf进行重新排列 2 static void 3 evbuffer_align(struct evbuffer *buf) 4 { 5 // 整个buffer 6 //| totallen | 7 //|--------------|-----------|-------------------------------------------| 8 //|misalign(偏移)|off(数据区)|datlen(需要加入的数据长度),大于totallen | 9 // | | 10 // / 11 // 整个buffer 12 //| totallen | 13 //|-----------|---------------------------------------------------------| 14 //|off(数据区) |datlen(需要加入的数据长度),大于totallen | 15 16 17 //缓冲区数据前移 18 //从buf->buffer拷贝off个字节到buf的orig_buffer 19 memmove(buf->orig_buffer, buf->buffer, buf->off); 20 21 //缓冲区数据起始位置变为buf起始位置 22 buf->buffer = buf->orig_buffer; 23 24 //偏移置为0 25 buf->misalign = 0; 26 }
4.evbuffer_expand
1 /* Expands the available space in the event buffer to at least datlen */ 2 //内存扩展 3 int 4 evbuffer_expand(struct evbuffer *buf, size_t datlen) 5 { 6 // 整个buffer 7 //| totallen | 8 //|--------------|-----------|-------------------------------------------| 9 //|misalign(偏移) |off(数据区) |datlen(需要加入的数据长度),大于totallen | 10 // 11 12 //首先判断是否需要扩展 13 size_t need = buf->misalign + buf->off + datlen; 14 15 //如果need小于totallen,无需扩展 16 /* If we can fit all the data, then we don't have to do anything */ 17 if (buf->totallen >= need) 18 return (0); 19 20 /* 21 * If the misalignment fulfills our data needs, we just force an 22 * alignment to happen. Afterwards, we have enough space. 23 */ 24 //如果偏移大于datlen, 25 if (buf->misalign >= datlen) { 26 //buf进行重新排列 27 evbuffer_align(buf); 28 } else { 29 //偏移小于datlen,数据元素大于totallen,需要重新分配内存 30 void *newbuf; 31 size_t length = buf->totallen; 32 33 //如果length小于256,length设置256 34 if (length < 256) 35 length = 256; 36 //如果length还是小于need,length扩大2倍直到不小于need 37 while (length < need) 38 length <<= 1; 39 40 //如果有偏移,先重新排列 41 if (buf->orig_buffer != buf->buffer) 42 evbuffer_align(buf); 43 //重新分配内存 44 if ((newbuf = realloc(buf->buffer, length)) == NULL) 45 return (-1); 46 //orig_buffer,buffer都赋值为新地址newbuf 47 buf->orig_buffer = buf->buffer = newbuf; 48 //总长度totallen为length 49 buf->totallen = length; 50 } 51 52 return (0); 53 }
5.所有代码注释
1 /* 2 * Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifdef HAVE_CONFIG_H 29 #include "config.h" 30 #endif 31 32 #ifdef WIN32 33 #include <winsock2.h> 34 #include <windows.h> 35 #endif 36 37 #ifdef HAVE_VASPRINTF 38 /* If we have vasprintf, we need to define this before we include stdio.h. */ 39 #define _GNU_SOURCE 40 #endif 41 42 #include <sys/types.h> 43 44 #ifdef HAVE_SYS_TIME_H 45 #include <sys/time.h> 46 #endif 47 48 #ifdef HAVE_SYS_IOCTL_H 49 #include <sys/ioctl.h> 50 #endif 51 52 #include <assert.h> 53 #include <errno.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #ifdef HAVE_STDARG_H 58 #include <stdarg.h> 59 #endif 60 #ifdef HAVE_UNISTD_H 61 #include <unistd.h> 62 #endif 63 64 #include "event.h" 65 #include "config.h" 66 #include "evutil.h" 67 #include "./log.h" 68 69 //创建evbuffer 70 struct evbuffer * 71 evbuffer_new(void) 72 { 73 struct evbuffer *buffer; 74 75 buffer = calloc(1, sizeof(struct evbuffer)); 76 77 return (buffer); 78 } 79 80 //释放evbuffer 81 void 82 evbuffer_free(struct evbuffer *buffer) 83 { 84 if (buffer->orig_buffer != NULL) 85 free(buffer->orig_buffer); 86 free(buffer); 87 } 88 89 /* 90 * This is a destructive add. The data from one buffer moves into 91 * the other buffer. 92 */ 93 94 //交换evbuffer 95 #define SWAP(x,y) do { 96 (x)->buffer = (y)->buffer; 97 (x)->orig_buffer = (y)->orig_buffer; 98 (x)->misalign = (y)->misalign; 99 (x)->totallen = (y)->totallen; 100 (x)->off = (y)->off; 101 } while (0) 102 103 //evbuffer数据交换,outhuf与inbuf 104 int 105 evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) 106 { 107 int res; 108 109 //如果outbuf没有数据元素 110 /* Short cut for better performance */ 111 if (outbuf->off == 0) { 112 struct evbuffer tmp; 113 size_t oldoff = inbuf->off; 114 115 //交换缓冲区 116 /* Swap them directly */ 117 SWAP(&tmp, outbuf); 118 SWAP(outbuf, inbuf); 119 SWAP(inbuf, &tmp); 120 121 /* 122 * Optimization comes with a price; we need to notify the 123 * buffer if necessary of the changes. oldoff is the amount 124 * of data that we transfered from inbuf to outbuf 125 */ 126 //如果现在的数据元素长度不等于以前inbuf中的数据元素长度,并且有回调函数的话, 127 //交换后inbuf调用回调函数,告诉现在数据元素长度已经改变等信息 128 if (inbuf->off != oldoff && inbuf->cb != NULL) 129 (*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg); 130 //原来inbuf数据元素个数不为0,且有回调函数。交换后的outbuf调用回调。 131 if (oldoff && outbuf->cb != NULL) 132 (*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg); 133 134 return (0); 135 } 136 //如果原来的outbuf中有数据元素,把inbuf中的数据元素加入进来 137 res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off); 138 if (res == 0) { 139 //res为零,成功将inbuf的数据元素加入到outbuf中来,所以可以将inbuf中的数据全部排出清空。 140 /* We drain the input buffer on success */ 141 evbuffer_drain(inbuf, inbuf->off); 142 } 143 144 return (res); 145 } 146 147 //将数据格式化后添加到buf中 148 int 149 evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) 150 { 151 char *buffer; 152 size_t space; 153 size_t oldoff = buf->off; 154 int sz; 155 va_list aq; 156 157 /* make sure that at least some space is available */ 158 //确保至少有一些空间,这里看看有没有64字节容量。 159 evbuffer_expand(buf, 64); 160 for (;;) { 161 size_t used = buf->misalign + buf->off; 162 buffer = (char *)buf->buffer + buf->off; 163 assert(buf->totallen >= used); 164 space = buf->totallen - used; 165 166 #ifndef va_copy 167 #define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list)) 168 #endif 169 va_copy(aq, ap); 170 //返回写入buffer后面的字节数 171 sz = evutil_vsnprintf(buffer, space, fmt, aq); 172 173 va_end(aq); 174 175 if (sz < 0) 176 return (-1); 177 //如果格式化的数据字节数小于剩余的容量 178 if ((size_t)sz < space) { 179 buf->off += sz; 180 if (buf->cb != NULL) 181 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); 182 return (sz); 183 } 184 //到这边说明容量不够,需要调整 185 if (evbuffer_expand(buf, sz + 1) == -1) 186 return (-1); 187 188 } 189 /* NOTREACHED */ 190 } 191 192 //将数据格式化后添加到buf中 193 int 194 evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) 195 { 196 int res = -1; 197 va_list ap; 198 199 va_start(ap, fmt); 200 res = evbuffer_add_vprintf(buf, fmt, ap); 201 va_end(ap); 202 203 return (res); 204 } 205 206 /* Reads data from an event buffer and drains the bytes read */ 207 //从buf中读出datlen个字节存到data开始地址中 208 int 209 evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen) 210 { 211 size_t nread = datlen; 212 //最多只能读出缓冲区中所有数据 213 if (nread >= buf->off) 214 nread = buf->off; 215 //从buf->buffer地址的起始位置拷贝nread个字节到data开始的地址 216 memcpy(data, buf->buffer, nread); 217 //将nread个字节的数据排出缓冲区 218 evbuffer_drain(buf, nread); 219 220 return (nread); 221 } 222 223 /* 224 * Reads a line terminated by either ' ', ' ' or ' ' or ' '. 225 * The returned buffer needs to be freed by the called. 226 */ 227 228 //从缓冲区中读出一行 229 char * 230 evbuffer_readline(struct evbuffer *buffer) 231 { 232 //缓冲数据区的起始地址 233 u_char *data = EVBUFFER_DATA(buffer); 234 //缓冲区数据长度 235 size_t len = EVBUFFER_LENGTH(buffer); 236 char *line; 237 unsigned int i; 238 239 //读到 或者 240 for (i = 0; i < len; i++) { 241 if (data[i] == ' ' || data[i] == ' ') 242 break; 243 } 244 //没读到回车或者换行,退出 245 if (i == len) 246 return (NULL); 247 //分配i+1字节内存,最后 结尾 248 if ((line = malloc(i + 1)) == NULL) { 249 fprintf(stderr, "%s: out of memory ", __func__); 250 return (NULL); 251 } 252 //从data起始地址开始复制i个字节到line中 253 memcpy(line, data, i); 254 line[i] = '