1、前言
最近项目中用到一个环形缓冲区(ring buffer),代码是由linux内核的kfifo改过来的。缓冲区在文件系统中经常用到,通过缓冲区缓解cpu读写内存和读写磁盘的速度。例如一个进程A产生数据发给另外一个进程B,进程B需要对进程A传的数据进行处理并写入文件,如果B没有处理完,则A要延迟发送。为了保证进程A减少等待时间,可以在A和B之间采用一个缓冲区,A每次将数据存放在缓冲区中,B每次冲缓冲区中取。这是典型的生产者和消费者模型,缓冲区中数据满足FIFO特性,因此可以采用队列进行实现。Linux内核的kfifo正好是一个环形队列,可以用来当作环形缓冲区。生产者与消费者使用缓冲区如下图所示:
环形缓冲区的详细介绍及实现方法可以参考http://en.wikipedia.org/wiki/Circular_buffer,介绍的非常详细,列举了实现环形队列的几种方法。环形队列的不便之处在于如何判断队列是空还是满。维基百科上给三种实现方法。
2、linux 内核kfifo
kfifo设计的非常巧妙,代码很精简,对于入队和出对处理的出人意料。首先看一下kfifo的数据结构:
struct kfifo { unsigned char *buffer; /* the buffer holding the data */ unsigned int size; /* the size of the allocated buffer */ unsigned int in; /* data is added at offset (in % size) */ unsigned int out; /* data is extracted from off. (out % size) */ spinlock_t *lock; /* protects concurrent modifications */ };
kfifo提供的方法有:
1 //根据给定buffer创建一个kfifo 2 struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size, 3 gfp_t gfp_mask, spinlock_t *lock); 4 //给定size分配buffer和kfifo 5 struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, 6 spinlock_t *lock); 7 //释放kfifo空间 8 void kfifo_free(struct kfifo *fifo) 9 //向kfifo中添加数据 10 unsigned int kfifo_put(struct kfifo *fifo, 11 const unsigned char *buffer, unsigned int len) 12 //从kfifo中取数据 13 unsigned int kfifo_put(struct kfifo *fifo, 14 const unsigned char *buffer, unsigned int len) 15 //获取kfifo中有数据的buffer大小 16 unsigned int kfifo_len(struct kfifo *fifo)
定义自旋锁的目的为了防止多进程/线程并发使用kfifo。因为in和out在每次get和out时,发生改变。初始化和创建kfifo的源代码如下:
1 struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size, 2 gfp_t gfp_mask, spinlock_t *lock) 3 { 4 struct kfifo *fifo; 6 /* size must be a power of 2 */ 7 BUG_ON(!is_power_of_2(size)); 9 fifo = kmalloc(sizeof(struct kfifo), gfp_mask); 10 if (!fifo) 11 return ERR_PTR(-ENOMEM); 13 fifo->buffer = buffer; 14 fifo->size = size; 15 fifo->in = fifo->out = 0; 16 fifo->lock = lock; 17 18 return fifo; 19 } 20 struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock) 21 { 22 unsigned char *buffer; 23 struct kfifo *ret; 29 if (!is_power_of_2(size)) { 30 BUG_ON(size > 0x80000000); 31 size = roundup_pow_of_two(