kfifo 是 Linux 内核中提供的一个轻量级、高效的先进先出(FIFO)无锁环形缓冲区实现(定义在 <linux/kfifo.h> 中)。它专门用于单生产者-单消费者场景下的数据传输与异步解耦。
struct __kfifo { unsigned int in; // 写入位置索引(累加值) unsigned int out; // 读取位置索引(累加值) unsigned int mask; // 缓冲区大小减 1(即 size - 1,用于位与运算取模) unsigned int esize; // 每个元素的大小 void *data; // 动态分配的缓冲区指针};
下面都是一些宏
// include/linux/kfifo.h//编译时静态定义并初始化一个 kfifoDEFINE_KFIFO(fifo, type, size)kfifo_init(fifo, buffer, size)//运行时动态分配内存并初始化kfifo_alloc(fifo, size, gfp_mask)//释放动态分配的kfifo内存kfifo_free(fifo)//放数据进入fifokfifo_put(fifo, val)kfifo_in(fifo, buf, n)//从fifo中获取数据kfifo_get(fifo, val)kfifo_out(fifo, buf, n)//返回fifo中可存放的数量(剩余空间)kfifo_avail(fifo)//判断 fifo 是否已满kfifo_is_full(fifo)//判断 fifo 是否为空kfifo_is_empty(fifo)
Linux:5.10
int __kfifo_init(struct __kfifo *fifo, void *buffer, unsigned int size, size_t esize){ size /= esize; //大小要是2的幂次方 if (!is_power_of_2(size)) size = rounddown_pow_of_two(size); fifo->in = 0; fifo->out = 0; fifo->esize = esize; //每个元素大小 fifo->data = buffer; //buffer指针 if (size < 2) { fifo->mask = 0; return -EINVAL; } fifo->mask = size - 1; return 0;}
静态初始化 fifo,需要自己提供 buffer 来初始化 kfifo 结构体。如果大小不是 2 的幂次方,它会自动降为 2 的幂次方的大小。
int __kfifo_alloc(struct __kfifo *fifo, unsigned int size, size_t esize, gfp_t gfp_mask){ /* * round up to the next power of 2, since our 'let the indices * wrap' technique works only in this case. */ size = roundup_pow_of_two(size); fifo->in = 0; fifo->out = 0; fifo->esize = esize; if (size < 2) { fifo->data = NULL; fifo->mask = 0; return -EINVAL; } //动态分配buffer空间 fifo->data = kmalloc_array(esize, size, gfp_mask); if (!fifo->data) { fifo->mask = 0; return -ENOMEM; } fifo->mask = size - 1; return 0;}
动态初始化 fifo,动态分配 buffer 空间,这是它和__kfifo_init 的区别。
unsigned int __kfifo_in(struct __kfifo *fifo, const void *buf, unsigned int len){ unsigned int l; //获取未使用的buffer大小 l = kfifo_unused(fifo); //最多写入未使用的大小 if (len > l) len = l; //写入 kfifo_copy_in(fifo, buf, len, fifo->in); //递增写入索引 fifo->in += len; return len;}
写入数据到 fifo 的 buffer 中
staticvoidkfifo_copy_in(struct __kfifo *fifo, constvoid *src, unsigned int len, unsigned int off){ unsigned int size = fifo->mask + 1; unsigned int esize = fifo->esize; unsigned int l; //写入索引的位置 off &= fifo->mask; if (esize != 1) { off *= esize; size *= esize; len *= esize; } l = min(len, size - off); //写到末尾位置 memcpy(fifo->data + off, src, l); //到开头位置写入(末尾不够写) memcpy(fifo->data, src + l, len - l); /* * make sure that the data in the fifo is up to date before * incrementing the fifo->in index counter */ smp_wmb();}
先确定要写入的索引位置
判断索引位置到末尾这些空间够不够存放要写入的数据
写入 (末尾不够写,就回到开头继续写)
unsigned int __kfifo_out(struct __kfifo *fifo, void *buf, unsigned int len){ //读取数据 len = __kfifo_out_peek(fifo, buf, len); //递增读取索引 fifo->out += len; return len;}
从 fifo 中读取数据出来
unsigned int __kfifo_out_peek(struct __kfifo *fifo, void *buf, unsigned int len){ unsigned int l; //未读取的数据大小 l = fifo->in - fifo->out; //可读取最大长度 if (len > l) len = l; kfifo_copy_out(fifo, buf, len, fifo->out); return len;}
staticvoidkfifo_copy_out(struct __kfifo *fifo, void *dst, unsigned int len, unsigned int off){ unsigned int size = fifo->mask + 1; unsigned int esize = fifo->esize; unsigned int l; //读取索引位置 off &= fifo->mask; if (esize != 1) { off *= esize; size *= esize; len *= esize; } l = min(len, size - off); //末尾读取 memcpy(dst, fifo->data + off, l); //回到开头读取 memcpy(dst + l, fifo->data, len - l); /* * make sure that the data is copied before * incrementing the fifo->out index counter */ smp_wmb();}
先确定要读取的索引位置
读取(先从末尾开始读取,如果还有需要读的,就回到开头读)