为什么需要用户态缓冲区
我们处理业务时,获取数据时可能有用户态和内核态的切换,这是很耗时的操作,如果我们吧从内核得到的数据放到应用层中,就能减少用户态到内核态的切换。
同时我们用用户态缓冲区解决了生产和消费的耗时不同而导致的问题(生产者大于消费者)。
同时我们的应用程序是以一个完整的数据包为单位处理数据的,而内核没有这个概念,我们就需要在用户态进行分包和粘包的处理。不同的协议和不用模型不能解决此问题,也就不影响用户态缓冲区的设计。
如何设计用户态缓冲区
我们的数据先到的先处理,所以采用队列的思想来设计。
有三种方案可选定长buf,ringbuffer以及chainbuffer。
这里介绍ringbuffer和chainbuffer
ringbuffer
我认为ringbuffer的主要核心思想为指向数据的头尾指针(只增不减)用取余的方式来经行操作,
代码如下:
#include <stdbool.h>
#include <assert.h>
#include <stdint.h>
#include <stdatomic.h>
#include "buffer.h"
struct ringbuffer_s {
uint32_t size;
uint32_t tail;
uint32_t head;
uint8_t * buf;
};
#define min(lth, rth) ((lth)<(rth)?(lth):(rth))
static inline int is_power_of_two(uint32_t num) {
if (num < 2) return 0;
return (num & (num - 1)) == 0;// 二的指数
}
static inline uint32_t roundup_power_of_two(uint32_t num) {
if (num == 0) return 2;
int i = 0;
for (; num != 0; i++)
num >>= 1;
return 1U << i;
}
buffer_t * buffer_new(uint32_t sz) {
if (!is_power_of_two(sz)) sz = roundup_power_of_two(sz);//是2的指数
buffer_t * buf = (buffer_t *)malloc(sizeof(buffer_t) + sz);
if (!buf) {
return NULL;
}
buf->size = sz;
buf->head = buf->tail = 0;
buf->buf = (uint8_t *)(buf + 1);
return buf;
}
void buffer_free(buffer_t *r) {
free(r);
r = NULL;
}
static uint32_t
rb_isempty(buffer_t *r) {
return r->head == r->tail;
}
static uint32_t
rb_isfull(buffer_t *r) {
return r->size == (r->tail - r->head);
}
static uint32_t
rb_len(buffer_t *r) {
return r->tail - r->head;
}
static uint32_t
rb_remain(buffer_t *r) {
return r->size - r->tail + r->head;
}
int buffer_add(buffer_t *r, const void *data, uint32_t sz) {
if (sz > rb_remain(r)) {
return -1;
}
uint32_t i;
i = min(sz, r->size - (r->tail & (r->size - 1)));//tail后面的内存和sz谁比较小 如果后面的内存小就要分段发送
memcpy(r->buf + (r->tail & (r->size - 1)), data, i);
memcpy(r->buf, data+i, sz-i);
r->tail += sz;
return 0;
}
int buffer_remove(buffer_t *r, void *data, uint32_t sz) {
assert(!rb_isempty(r));
uint32_t i;
sz = min(sz, r->tail - r->head); //在数据中占的内存
i = min(sz, r->size - (r->head & (r->size - 1)));
memcpy(data, r->buf+(r->head & (r->size - 1)), i);
memcpy(data+i, r->buf, sz-i);
r->head += sz;
return sz;
}
int buffer_drain(buffer_t *r, uint32_t sz) {//移动head? 删除数据
if (sz > rb_len(r))
sz = rb_len(r);
r->head += sz;
return sz;
}
// 找 buffer 中是否包含特殊字符串
int buffer_search(buffer_t *r, const char* sep, const int seplen) {
int i;
for (i = 0; i <= rb_len(r)-seplen; i++) {
int pos = (r->head + i) & (r->size - 1);
if (pos + seplen > r->size) {
if (memcmp(r->buf+pos, sep, r->size-pos))
return 0;
if (memcmp(r->buf, sep, pos+seplen-r->size) == 0) {
return i+seplen;
}
}
if (memcmp(r->buf+pos, sep, seplen) == 0) {
return i+seplen;
}
}
return 0;
}
uint32_t buffer_len(buffer_t *r) {
return rb_len(r);
}
uint8_t * buffer_write_atmost(buffer_t *r) {//正负转换 head小于tail
uint32_t rpos = r->head & (r->size - 1);
uint32_t wpos = r->tail & (r->size - 1);
if (wpos < rpos) {
uint8_t* temp = (uint8_t *)malloc(r->size * sizeof(uint8_t));
memcpy(temp, r->buf+rpos, r->size - rpos);
memcpy(temp+r->size-rpos, r->buf, wpos);
free(r->buf);
r->buf = temp;
return r->buf;
}
return r->buf + rpos;
}
核心为缓冲区的size为2的倍数,以及通过对2取余来进行对数据的操作和tail和head永远只增加不减少。核心函数为 is_power_of_two 、 roundup_power_of_two 以及(r->head & (r->size - 1)))取余,形成逻辑的环形
chainbuffer
结构如图所示
#ifndef _chain_buffer_h
#define _chain_buffer_h
#include <stdint.h>
typedef struct buf_chain_s buf_chain_t;
typedef struct buffer_s buffer_t;
// struct buf_chain_s {
// struct buf_chain_s *next;
// uint32_t buffer_len;
// uint32_t misalign;
// uint32_t off;
// uint8_t *buffer;
// };
// struct buffer_s {
// buf_chain_t *first;
// buf_chain_t *last;
// buf_chain_t **last_with_datap;
// uint32_t total_len;
// uint32_t last_read_pos; // for sep read
// };
buffer_t * buffer_new(uint32_t sz);
uint32_t buffer_len(buffer_t *buf);
int buffer_add(buffer_t *buf, const void *data, uint32_t datlen);
int buffer_remove(buffer_t *buf, void *data, uint32_t datlen);
int buffer_drain(buffer_t *buf, uint32_t len);
void buffer_free(buffer_t *buf);
int buffer_search(buffer_t *buf, const char* sep, const int seplen);
uint8_t * buffer_write_atmost(buffer_t *p);
#endif
#include <string.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "buffer.h"
struct buf_chain_s {
struct buf_chain_s *next;
uint32_t buffer_len;//分配的总大小减去头
uint32_t misalign;//错位
uint32_t off;
uint8_t *buffer;
};
struct buffer_s {
buf_chain_t *first;
buf_chain_t *last;
buf_chain_t **last_with_datap;
uint32_t total_len;//总长度
uint32_t last_read_pos; // for sep read 最后读到哪儿
};
#define CHAIN_SPACE_LEN(ch) ((ch)->buffer_len - ((ch)->misalign + (ch)->off))
#define MIN_BUFFER_SIZE 1024
#define MAX_TO_COPY_IN_EXPAND 4096
#define BUFFER_CHAIN_MAX_AUTO_SIZE 4096
#define MAX_TO_REALIGN_IN_EXPAND 2048
#define BUFFER_CHAIN_MAX 16*1024*1024 // 16M
#define BUFFER_CHAIN_EXTRA(t, c) (t *)((buf_chain_t *)(c) + 1)
#define BUFFER_CHAIN_SIZE sizeof(buf_chain_t)
uint32_t
buffer_len(buffer_t *buf) {
return buf->total_len;
}
buffer_t *
buffer_new(uint32_t sz) {//sz的作用
(void)sz;
buffer_t * buf = (buffer_t *) malloc(sizeof(buffer_t));
if (!buf) {
return NULL;
}
memset(buf, 0, sizeof(*buf));
buf->last_with_datap = &buf->first;//最后的数据 删除数据用
return buf;
}
static buf_chain_t *
buf_chain_new(uint32_t size) {
buf_chain_t *chain;
uint32_t to_alloc;
if (size > BUFFER_CHAIN_MAX - BUFFER_CHAIN_SIZE)
return (NULL);
size += BUFFER_CHAIN_SIZE;
if (size < BUFFER_CHAIN_MAX / 2) {
to_alloc = MIN_BUFFER_SIZE;
while (to_alloc < size) {
to_alloc <<= 1;
}
} else {
to_alloc = size;
}
if ((chain = malloc(to_alloc)) == NULL)
return (NULL);
memset(chain, 0, BUFFER_CHAIN_SIZE);
chain->buffer_len = to_alloc - BUFFER_CHAIN_SIZE;
chain->buffer = BUFFER_CHAIN_EXTRA(uint8_t, chain);
return (chain);
}
static void
buf_chain_free_all(buf_chain_t *chain) {
buf_chain_t *next;
for (; chain; chain = next) {
next = chain->next;
free(chain);
}
}
void
buffer_free(buffer_t *buf) {
buf_chain_free_all(buf->first);
}
static buf_chain_t **
free_empty_chains(buffer_t *buf) {
buf_chain_t **ch = buf->last_with_datap;
while ((*ch) && (*ch)->off != 0)//有数据 往下走
ch = &(*ch)->next;
if (*ch) {//如果此时数据没有取除去 被删除
buf_chain_free_all(*ch);
*ch = NULL;
}
return ch;
}
static void
buf_chain_insert(buffer_t *buf, buf_chain_t *chain) {
if (*buf->last_with_datap == NULL) {
buf->first = buf->last = chain;
} else {
buf_chain_t **chp;
chp = free_empty_chains(buf);//找到最后一个 复用
*chp = chain;
if (chain->off)
buf->last_with_datap = chp;
buf->last = chain;
}
buf->total_len += chain->off;
}
static inline buf_chain_t *
buf_chain_insert_new(buffer_t *buf, uint32_t datlen) {
buf_chain_t *chain;
if ((chain = buf_chain_new(datlen)) == NULL)
return NULL;
buf_chain_insert(buf, chain);
return chain;
}
static int
buf_chain_should_realign(buf_chain_t *chain, uint32_t datlen)//调整 realign
{
return chain->buffer_len - chain->off >= datlen &&
(chain->off < chain->buffer_len / 2) &&
(chain->off <= MAX_TO_REALIGN_IN_EXPAND);
}
static void
buf_chain_align(buf_chain_t *chain) {
memmove(chain->buffer, chain->buffer + chain->misalign, chain->off);
chain->misalign = 0;//错位
}
int buffer_add(buffer_t *buf, const void *data_in, uint32_t datlen) {
buf_chain_t *chain, *tmp;
const uint8_t *data = data_in;//chai*
uint32_t remain, to_alloc;
int result = -1;
if (datlen > BUFFER_CHAIN_MAX - buf->total_len) {
goto done;
}
if (*buf->last_with_datap == NULL) {
chain = buf->last;
} else {
chain = *buf->last_with_datap;
}
if (chain == NULL) {
chain = buf_chain_insert_new(buf, datlen);
if (!chain)
goto done;
}
remain = chain->buffer_len - chain->misalign - chain->off;//?off是大小还是指针 大小
if (remain >= datlen) {
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 (buf_chain_should_realign(chain, datlen)) {//是否需要修复 没考虑调整后的remain是否大于数据长度
buf_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;
} // 不需要修复而且remain小于数据长度
to_alloc = chain->buffer_len;
if (to_alloc <= BUFFER_CHAIN_MAX_AUTO_SIZE/2)
to_alloc <<= 1;
if (datlen > to_alloc)
to_alloc = datlen;
tmp = buf_chain_new(to_alloc);
if (tmp == NULL)
goto done;
if (remain) {// 为什么要分开发送
memcpy(chain->buffer + chain->misalign + chain->off, data, remain);//长度为什么是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;
buf_chain_insert(buf, tmp);
// buf->n_add_for_cb += datlen;
out:
result = 0;
done:
return result;
}
static uint32_t
buf_copyout(buffer_t *buf, void *data_out, uint32_t datlen) {
buf_chain_t *chain;
char *data = data_out;
uint32_t nread;
chain = buf->first;
if (datlen > buf->total_len)
datlen = buf->total_len;
if (datlen == 0)
return 0;
nread = datlen;
while (datlen && datlen >= chain->off) {//要取的数据在多个chain中
uint32_t copylen = chain->off;
memcpy(data,
chain->buffer + chain->misalign,
copylen);
data += copylen;
datlen -= copylen;
chain = chain->next;
}
if (datlen) {
memcpy(data, chain->buffer + chain->misalign, datlen);
}
return nread;
}
static inline void
ZERO_CHAIN(buffer_t *dst) {
dst->first = NULL;
dst->last = NULL;
dst->last_with_datap = &(dst)->first;
dst->total_len = 0;
}
int buffer_drain(buffer_t *buf, uint32_t len) {//删除len长度的数据
buf_chain_t *chain, *next;
uint32_t remaining, old_len;
old_len = buf->total_len;
if (old_len == 0)
return 0;
if (len >= old_len) {
len = old_len;
for (chain = buf->first; chain != NULL; chain = next) {
next = chain->next;
free(chain);
}
ZERO_CHAIN(buf);
} else {//len<oldlen
buf->total_len -= len;//oldlen 为剩余空间
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)
buf->last_with_datap = &buf->first;
free(chain);
}
buf->first = chain;
chain->misalign += remaining;
chain->off -= remaining;
}
// buf->n_del_for_cb += len;
return len;
}
int buffer_remove(buffer_t *buf, void *data_out, uint32_t datlen) {
uint32_t n = buf_copyout(buf, data_out, datlen);
if (n > 0) {
if (buffer_drain(buf, n) < 0)
n = -1;
}
return (int)n;
}
//check 检查
static bool
check_sep(buf_chain_t * chain, int from, const char *sep, int seplen) {//从from到off有无sep字符串
for (;;) {
int sz = chain->off - from;
if (sz >= seplen) {//在一个chainzhong
return memcmp(chain->buffer + chain->misalign + from, sep, seplen) == 0;
}
if (sz > 0) {//sz<seplen 在多个chain中
if (memcmp(chain->buffer + chain->misalign + from, sep, sz)) {
return false;
}
}
chain = chain->next;
sep += sz;
seplen -= sz;
from = 0;
}
}
//搜索
int buffer_search(buffer_t *buf, const char* sep, const int seplen) {
buf_chain_t *chain;
int i;
chain = buf->first;
if (chain == NULL)
return 0;
int bytes = chain->off;
while (bytes <= buf->last_read_pos) {//得到之前读到的位置
chain = chain->next;
if (chain == NULL)
return 0;
bytes += chain->off;
}
bytes -= buf->last_read_pos;//该chain剩余的字节
int from = chain->off - bytes;
for (i = buf->last_read_pos; i <= buf->total_len - seplen; i++) {
if (check_sep(chain, from, sep, seplen)) {
buf->last_read_pos = 0;
return i+seplen;
}
++from;
--bytes;
if (bytes == 0) {
chain = chain->next;
from = 0;
if (chain == NULL)
break;
bytes = chain->off;
}
}
buf->last_read_pos = i;
return 0;
}
uint8_t * buffer_write_atmost(buffer_t *p) {//将所有的数据集合起来
buf_chain_t *chain, *next, *tmp, *last_with_data;
uint8_t *buffer;
uint32_t remaining;
int removed_last_with_data = 0;
int removed_last_with_datap = 0;
chain = p->first;
uint32_t size = p->total_len;
if (chain->off >= size) {
return chain->buffer + chain->misalign;
}
remaining = size - chain->off;
for (tmp=chain->next; tmp; tmp=tmp->next) {//找到已经使用的内存和大于总数据
if (tmp->off >= (size_t)remaining)
break;
remaining -= tmp->off;
}
if (chain->buffer_len - chain->misalign >= (size_t)size) {//off<size
/* already have enough space in the first chain */
size_t old_off = chain->off;
buffer = chain->buffer + chain->misalign + chain->off;// 位置
tmp = chain;
tmp->off = size;
size -= old_off;//还位写的数据,在后面的结构体可以转到现在的结构体
chain = chain->next;
} else {// 直接新建一个足够大的
if ((tmp = buf_chain_new(size)) == NULL) {
return NULL;
}
buffer = tmp->buffer;
tmp->off = size;
p->first = tmp;
}
last_with_data = *p->last_with_datap;
for (; chain != NULL && (size_t)size >= chain->off; chain = next) {
next = chain->next;
if (chain->buffer) {
memcpy(buffer, chain->buffer + chain->misalign, chain->off);
size -= chain->off;
buffer += chain->off;
}
if (chain == last_with_data)//?
removed_last_with_data = 1;
if (&chain->next == p->last_with_datap)
removed_last_with_datap = 1;
free(chain);
}
if (chain != NULL) {
memcpy(buffer, chain->buffer + chain->misalign, size);
chain->misalign += size;
chain->off -= size;
} else {
p->last = tmp;
}
tmp->next = chain;
if (removed_last_with_data) {
p->last_with_datap = &p->first;
} else if (removed_last_with_datap) {
if (p->first->next && p->first->next->off)
p->last_with_datap = &p->first->next;
else
p->last_with_datap = &p->first;
}
return tmp->buffer + tmp->misalign;
}