概述 链接到标题
环形缓存是嵌入式软件开发中常用的数据结构之一, 其内容按先进先出的顺序存储,用于实现数据的异步「流式」复制。Zephyr 提供了一个 struct ring_buf
抽象来管理此类数据结构。 Zephyr 中的驱动 ( UART, Modem,I2S 等 ) ,shell, usb, net, tracking 都有使用环形缓存。
Zephyr 的环形缓存实现支持两种模式:
- 字节模式
- 数据项模式
使用限制:
- 同一个缓存区内两种模式不能混用。
- 环形缓存最大不能超过
#define RING_BUFFER_MAX_SIZE 0x80000000U
- 本身不支持并发
- 数据项最大不超过 1020 字节
初始化 链接到标题
struct ring_buf
用于管理环形缓存,其内部数据对用户隐藏,该结构放置在用户可访问的内存中,必须用 ring_buf_init ( )
或 ring_buf_item_init ( )
初始化后才能使用。初始化时需要用户提供一段内存作为环形缓存实际存储数据的空间。
字节模式
字节模式使用 ring_buf_init ( )
进行初始化,示例如下:
# define RING_BUF_BYTE_SIZE 320
struct ring_buf rb_byte;
uint8_t ring_buf_byte [RING_BUF_BYTE_SIZE];
ring_buf_init ( &rb_byte, RING_BUF_BYTE_SIZE, ring_buf_byte ) ;
也可以使用宏静态的定义&初始化环形缓存,下面这段代码的效果和上面一致
RING_BUF_DECLARE ( rb_byte, RING_BUF_BYTE_SIZE ) ;
数据项模式
数据项模式使用 ring_buf_item_init ( )
进行初始化,其初始化的环形缓存最小操作单位为字 ( 4 字节 ) ,示例如下:
# define RING_BUF_ITEM_SIZE 128
struct ring_buf rb_item;
uint32_t ring_buf_item [RING_BUF_ITEM_SIZE];
ring_buf_item_init ( &rb_item, RING_BUF_ITEM_SIZE, ring_buf_item ) ;
也可以使用宏静态的定义&初始化环形缓存,下面这段代码的效果和上面一致
RING_BUF_ITEM_DECLARE ( rb_item, RING_BUF_ITEM_SIZE ) ;
操作 链接到标题
写入数据 链接到标题
字节模式
通过调用 ring_buf_put ( )
将字节复制到字节模式环形缓冲区, 当数据长度超过缓存区的空闲空间长度时,会拷贝部分数据,返回值为拷贝的数据长度。示例如下
uint8_t my_data [MY_RING_BUF_BYTES];
uint32_t ret;
ret = ring_buf_put ( &ring_buf, my_data, MY_RING_BUF_BYTES ) ;
if ( ret != MY_RING_BUF_BYTES ) {
//当数据长度超过缓存区的长度时,会拷贝部分数据
//这里需要做未完整拷贝的处理,例如进行等待,再次拷贝剩余数据
}
除此之外,字节模式的环形缓存还支持类似 MPSC/SPSC 的请求+提交两部操作,这样可以减少一次拷贝时间,提高效率,下面示例的操作等同于 put
uint32_t size;
uint32_t rx_size;
uint8_t *data;
int err;
//从环形缓存中请求空间大小 MY_RING_BUF_BYTES,空间地址放在 data 内,返回值为实际请求空间的大小
size = ring_buf_put_claim ( &ring_buf, &data, MY_RING_BUF_BYTES ) ;
//从 UART 将数据接收到从环形缓存请求的空间内
rx_size = uart_rx ( data, size ) ;
//提交数据
err = ring_buf_put_finish ( &ring_buf, rx_size ) ;
if ( err != 0 ) {
//正常情况下不应该发生,除非传入了错误的 rx_size 大于请求的 size
}
数据项模式
数据项模式只支持复制方式,通过调用 ring_buf_item_put ( )
将数据项复制到环形缓冲区内, 当数据长度超过缓存区的空闲空间长度时,不会发生拷贝,直接返回 -EMSGSIZE
。
每一个数据项在环形缓存会多占用 32bit 用作该项的管理
struct ring_element {
uint32_t type :16; //应用指定数据项的类型
uint32_t length :8; //数据项的字长
uint32_t value :8; //应用指定值
};
由于数据项的字长只用了 8bit 存储,因此数据项最大长度为 255*4=1020 字节。
示例如下:
uint32_t data [MY_DATA_WORDS];
int ret;
//写入数据项 data,类型为 TYPE_FOO,值为 0. 类型和值都为应用自定,用于标识数据项
ret = ring_buf_item_put ( &ring_buf, TYPE_FOO, 0, data, MY_DATA_WORDS ) ;
if ( ret == -EMSGSIZE ) {
//空间不够
...
}
注意:不要写入空数据项 ( size = 0 )
读取数据 链接到标题
字节模式
通过调用 ring_buf_gut ( )
将数据从字节模式环形缓冲区读出, 当需求量超过缓存区的有效数据时,会拷贝有效数据,返回值为拷贝的数据长度。示例如下
uint8_t my_data [MY_DATA_BYTES];
size_t ret;
ret = ring_buf_get ( &ring_buf, my_data, sizeof ( my_data )) ;
if ( ret != sizeof ( my_data )) {
//数据没读够的操作,可以等待到有数据了继续读
}
除此之外,字节模式的环形缓存还支持类似 MPSC/SPSC 的请求+释放两部操作,这样可以减少一次拷贝时间,提高效率,下面示例的操作等同于 get
uint32_t size;
uint32_t proc_size;
uint8_t *data;
int err;
//请求数据,请求到数据的地址放在 data, 返回值为有效数据长度
size = ring_buf_get_claim ( &ring_buf, &data, MY_RING_BUF_BYTES ) ;
//处理数据
proc_size = process ( data, size ) ;
//通知数据处理完成,释放已处理数据的空间
err = ring_buf_get_finish ( &ring_buf, proc_size ) ;
if ( err != 0 ) {
//正常情况下不应该发生,除非传入了错误的 proc_size 大于请求的 size
...
}
数据项模式
数据项模式只支持复制方式,通过调用 ring_buf_item_get ( )
将数据项从环形缓冲区复制到用户内存空间中, 当获取的数据项长度大于用户给予的空间时读取会失败,直接返回 -EMSGSIZE
。 如果没有数据项可读会返回 -EAGAIN
,获取数据项时能得到写入该数据项时的类型和值。
示例如下:
uint32_t my_data [MY_DATA_WORDS];
uint16_t my_type;
uint8_t my_value;
uint8_t my_size;
int ret;
my_size = MY_DATA_WORDS;
ret = ring_buf_item_get ( &ring_buf, &my_type, &my_value, my_data, &my_size ) ;
if ( ret == -EMSGSIZE ) {
printk ( "Buffer is too small, need %d uint32_t\n", my_size ) ;
} else if ( ret == -EAGAIN ) {
printk ( "Ring buffer is empty\n" ) ;
} else {
printk ( "Got item of type %u value &u of size %u dwords\n",
my_type, my_value, my_size ) ;
...
}
其它 API 链接到标题
除了上述 API 外,环形缓存还提供如下 API:
//Peek 数据,从环形缓存 buf 内拷贝长为 size 的数据到 data 内,返回实际的拷贝长度
//与 ring_buf_get 的差异是,ring_buf_peek 拷贝后数据仍然保留在环形缓存中,空间不会被释放
uint32_t ring_buf_peek ( struct ring_buf *buf, uint8_t *data, uint32_t size ) ;
//清空环形缓存中的数据
static inline void ring_buf_reset ( struct ring_buf *buf ) ;
//返回环形缓存 buf 的最大字节容量
static inline uint32_t ring_buf_capacity_get ( struct ring_buf *buf ) ;
//返回环形缓存 buf 中的有效数据字节大小
static inline uint32_t ring_buf_size_get ( struct ring_buf *buf ) ;
//返回环形缓存 buf 中的空闲空间字节大小
static inline uint32_t ring_buf_space_get ( struct ring_buf *buf ) ;
//返回环形缓存 buf 中的空闲空间字大小
static inline uint32_t ring_buf_item_space_get ( struct ring_buf *buf ) ;
//判断环形缓存是否为空
static inline bool ring_buf_is_empty ( struct ring_buf *buf ) ;
参考 链接到标题
https://docs.zephyrproject.org/3.5.0/kernel/data_structures/ring_buffers.html