环形缓冲区

什么是环形缓冲区 (Circular Buffers)


环形缓冲区是大小固定的含两个指针的环形 Buffer.

  1. head 指针:生产者将新数据插入缓冲区的位置点
  2. tail 指针 :消费者从缓冲区读取数据的位置点

一些特点


  1. head == tail 时, 缓冲区为空 (EMPTY)
  2. head < tail 时, 缓冲区为满 (FULL)
  3. 头索引在添加项时递增,尾索引在删除项时递增
  4. 尾部索引不会跳过头部索引,当到达缓冲区末尾时,两个索引都应该被包装为0,从而允许无限数量的数据流经缓冲区
  5. 通过将内存屏障与循环缓冲区结合使用,可以避免一些锁操作:
    • 使用一个单独的锁来控制对缓冲区两端的访问,从而允许同时填充和清空缓冲区
    • 使用原子计数器操作
      这有两个方面, 生产者填充缓冲区,消费者清空(读取、吃掉)缓冲区。在任何时间只有一件事,或者是填充缓冲区,或者是清空缓冲区,但是两边可以同时操作。

生产者


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <linux/circ_buf.h>
...

spin_lock(&producer_lock);
unsigned long head = buffer->head;
unsigned long tail = READ_ONCE(buffer->tail);


// 有空间可写

if(CIRC_SPACE(head, tail, buffer->size) >= 1) {
// got addr to store something

// insert one item into the head position of buffer

struct item* item = buffer[head];
produce_item(item);
smp_store_release(buffer->head, (head + 1) & (buffer->size -1 ));
// wake_up() will make sure that the head is committed before waking anyone up
wake_up(consumer);

}

spin_unlock(&producer_lock);

消费者


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
spin_lock(&consumer_lock);
// read index before reading contents at that index

unsigned long head = smp_load_acquire(buffer->head);
unsigned long tail = buffer->tail;


// 有数据可读

if(CIRC_CNT(head, tail, buffer->size) >= 1) {
// extract one item from the buffer
struct item* item = buffer[tail];
consume_ite(item);
// finish reading descriptor before incrementing tail
smp_store_release(buffer->tail, (tail + 1) & (buffer->size - 1));

}
spin_unlock(&consumer_lock);

这篇文章也是以前的笔记, 不知是自己写的还是在哪儿摘抄的, 过于久远.