用户态缓存:环形缓冲区(Ring Buffer)

目录

环形缓冲区(Ring Buffer)简介

为什么选择环形缓冲区?

代码解析

1. 头文件与类型定义

1.1 头文件保护符

1.2 包含必要的标准库

1.3 类型定义

2. 环形缓冲区结构体

2.1 结构体成员解释

3. 辅助宏与内联函数

3.1 min 宏

3.2 is_power_of_two 内联函数

3.3 roundup_power_of_two 内联函数

4. 环形缓冲区的基本操作

4.1 创建新缓冲区

4.2 获取缓冲区长度

4.3 释放缓冲区

4.4 添加数据到缓冲区

4.5 从缓冲区移除数据

4.6 清空缓冲区的一部分数据

4.7 在缓冲区中搜索特定字符串

4.8 获取写入缓冲区的可写指针

5. 其他辅助函数

5.1 判断缓冲区是否为空

5.2 判断缓冲区是否已满

5.3 获取缓冲区中剩余的空间

6. 代码中的关键概念与实现

6.1 环形地址计算

6.2 缓冲区大小为2的幂次方

6.3 双指针机制

7. 综合应用

7.1 在用户态缓存区中的应用

7.2 处理生产者与消费者速度不匹配

7.3 结合之前的内容

8. 总结


环形缓冲区(Ring Buffer)简介

环形缓冲区是一种高效的数据结构,广泛应用于生产者-消费者模型中。在网络通信中,尤其是用户态缓存区中,环形缓冲区通过循环使用固定大小的内存区域,减少数据移动和内存管理开销,提升数据传输效率。

为什么选择环形缓冲区?

  • 减少数据移动:数据在缓冲区中循环写入和读取,避免了频繁的数据拷贝操作。
  • 高效缓存管理:适用于高并发场景,能够快速响应数据的读写请求。
  • 简化内存管理:固定大小的缓冲区结构简化了内存分配和释放。

代码解析

让我们逐步解析环形缓冲区代码,理解其各个部分的功能和实现细节。

1. 头文件与类型定义

#ifndef _ringbuffer_h
#define _ringbuffer_h

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #include <limits.h>  // for uint_max
#include <stdint.h>
#include <unistd.h>

typedef struct ringbuffer_s buffer_t;

buffer_t * buffer_new(uint32_t sz);

uint32_t buffer_len(buffer_t *r);

void buffer_free(buffer_t *r);

int buffer_add(buffer_t *r, const void *data, uint32_t sz);

int buffer_remove(buffer_t *r, void *data, uint32_t sz);

int buffer_drain(buffer_t *r, uint32_t sz);

int buffer_search(buffer_t *r, const char* sep, const int seplen);

uint8_t * buffer_write_atmost(buffer_t *r);

#endif
1.1 头文件保护符
#ifndef _ringbuffer_h
#define _ringbuffer_h
...
#endif
  • 作用:防止头文件被多次包含,避免重复定义错误。
1.2 包含必要的标准库
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #include <limits.h>  // for uint_max
#include <stdint.h>
#include <unistd.h>
  • 标准库:提供了内存管理(mallocfree)、字符串操作(memcpy)、固定宽度整数类型(uint32_tuint8_t)等功能。
1.3 类型定义
typedef struct ringbuffer_s buffer_t;
  • 作用:为 struct ringbuffer_s 定义一个别名 buffer_t,简化后续代码的书写。

2. 环形缓冲区结构体

struct ringbuffer_s {
    uint32_t size;
    uint32_t tail;
    uint32_t head;
    uint8_t * buf;
};
2.1 结构体成员解释
  • size (uint32_t):缓冲区的总大小,以字节为单位。通常为2的幂次方,便于使用位运算实现环形效果。

  • tail (uint32_t):指向缓冲区的写入位置。每次添加数据时,tail 会递增。

  • head (uint32_t):指向缓冲区的读取位置。每次移除数据时,head 会递增。

  • buf (uint8_t *):指向实际数据存储区的指针。数据以字节形式存储。

3. 辅助宏与内联函数

#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;
}
3.1 min
#define min(lth, rth) ((lth)<(rth)?(lth):(rth))
  • 作用:返回两个值中的较小者,简化代码中的条件判断。
3.2 is_power_of_two 内联函数
static inline int is_power_of_two(uint32_t num) {
    if (num < 2) return 0;
    return (num & (num - 1)) == 0;
}
  • 作用:判断一个数是否为2的幂次方。
  • 原理:2的幂次方数在二进制中只有一个1,其减1后所有位都会变为1,按位与结果为0。
3.3 roundup_power_of_two 内联函数
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;
}
  • 作用:将一个数向上舍入到最近的2的幂次方。
  • 实现:通过不断右移操作,计算出需要的位数,然后使用位移生成对应的2的幂次方数。

4. 环形缓冲区的基本操作

4.1 创建新缓冲区
buffer_t * buffer_new(uint32_t sz) {
    if (!is_power_of_two(sz)) sz = roundup_power_of_two(sz);
    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;
}
  • 功能:分配并初始化一个新的环形缓冲区。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值