循环缓冲区,Linux内核中的循环缓冲区

Linux内核中的循环缓冲区(circular buffer)为解决某些特殊情况下的竞争问题提供了一种免锁的方法。这种特殊的情况就是当生产者和消费者都只有一个,而在其它情况下使用它也是必须要加锁的。
循环缓冲区定义在include/linux/kfifo.h中,如下:
struct kfifo {
unsigned char *buffer; // buffer指向存放数据的缓冲区
unsigned int size; // size是缓冲区的大小
unsigned int in; // in是写指针下标
unsigned int out; // out是读指针下标
spinlock_t *lock; // lock是加到struct kfifo上的自旋锁
};
(上面说的免锁不是免这里的锁,这个锁是必须的),防止多个进程并发访问此数据结构。当in==out时,说明缓冲区为空;当(in-out)==size时,说明缓冲区已满。
为kfifo提供的接口可以分为两类:
一类是满足上述情况下使用的,以双下划线开头,没有加锁的;
另一类是在不满足的条件下,即需要额外加锁的情况下使用的。
其实后一类只是在前一类的基础上进行加锁后的包装(也有一处进行了小的改进),实现中所加的锁是spin_lock_irqsave。
清空缓冲区的函数:
static inline void __kfifo_reset(struct kfifo *fifo);
static inline void kfifo_reset(struct kfifo *fifo);
这很简单,直接把读写指针都置为0即可。
向缓冲区里放入数据的接口是:
static inline unsigned int kfifo_put(struct kfifo *fifo, unsigned char *buffer, unsigned int len);
unsigned int __kfifo_put(struct kfifo *fifo, unsigned char *buffer, unsigned int len);
后者是在kernel/kfifo.c中定义的。这个接口是经过精心构造的,可以小心地避免一些边界情况。我们有必要一起来看一下它的具体实现。
1: /**
2: * __kfifo_put - puts some data into the FIFO, no locking version
3: * @fifo: the fifo to be used.
4: * @buffer: the data to be added.
5: * @len: the length of the data to be added.
6: *
7: * This function copies at most @len bytes from the @buffer into
8: * the FIFO depending _disibledevent=> 9: * bytes copied.
10: *
11: * Note that with _disibledevent=> 12: * writer, you don't need extra locking to use these functions.
13: */
14: unsigned int __kfifo_put(struct kfifo *fifo,
15: const unsigned char *buffer, unsigned int len)
16: {
17: unsigned int l;
18:
19: len = min(len, fifo->size - fifo->in + fifo->out);
20:
21: /*
22: * Ensure that we sample the fifo->out index -before- we
23: * start putting bytes into the kfifo.
24: */
25:
26: smp_mb();
27:
28: /* first put the data starting from fifo->in to buffer end */
29: l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
30: memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
31:
32: /* then put the rest (if any) at the beginning of the buffer */
33: memcpy(fifo->buffer, buffer + l, len - l);
34:
35: /*
36: * Ensure that we add the bytes to the kfifo -before-
37: * we update the fifo->in index.
38: */
39:
40: smp_wmb();
41:
42: fifo->in += len;
43:
44: return len;
45: }
46: EXPORT_SYMBOL(__kfifo_put);
Tags:  linux内核 循环缓冲区

延伸阅读

最新评论

发表评论