参考:https://blog.csdn.net/Bruno_Mars/article/details/100061793
kfifo源码:
https://elixir.bootlin.com/linux/v5.4.240/source/include/linux/kfifo.h#L118
https://elixir.bootlin.com/linux/v5.4.240/source/lib/kfifo.c#L113
kfifo smaple code:
https://elixir.bootlin.com/linux/v5.4.240/source/samples/kfifo
kfifo 特性
kfifo是内核里面的一个First In First Out数据结构
源码位于:
linux\lib\kfifo.c
linux\include\linux\kfifo.h
kfifo 实现了使用简单/性能高效的队列操作
只要满足以下要求, kfifo 操作便可以实现不加锁, 从而提高性能:
只有一个 reader 和 一个 writer,不调用 kfifo_reset(),如果有调用 kfifo_reset_out(), 只在出队线程中调用
而对于多个 writer 对应一个 reader 的情况, 只需要在 writer (入队线程)加锁即可
而对于多个 reader 对应一个 writer 的情况, 只需要在 reader (出队线程)加锁即可
源码中关于锁的相关介绍如下:
/* * Note about locking: There is no locking required until only one reader * and one writer is using the fifo and no kfifo_reset() will be called. * kfifo_reset_out() can be safely used, until it will be only called * in the reader thread. * For multiple writer and one reader there is only a need to lock the writer. * And vice versa for only one writer and multiple reader there is only a need * to lock the reader. */
kfifo 分为两种, 一种是普通的 kfifo, 一种是带长度记录的 kfiro_rec
对于 kfifo,内存 buffer 全部用来存放数据,如下图:

而对于 kfiro_rec,内存 buffer 在每次入队时都会用一个字节或两个字节存放数据的长度,如下图:

kfifo 适用于流数据的数据缓存, 一个线程进行数据入队, 另一个线程进行数据出队处理
kfifo_rec 适用于块数据的数据缓存, 一个线程进行一块块的数据入队, 另一个线程进行每次取一块数据进行
kfifo 结构体
struct __kfifo { unsigned int in; unsigned int out; unsigned int mask; unsigned int esize; void *data; }; #define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \ union { \ struct __kfifo kfifo; \ datatype *type; \ const datatype *const_type; \ char (*rectype)[recsize]; \ ptrtype *ptr; \ ptrtype const *ptr_const; \ } #define __STRUCT_KFIFO(type, size, recsize, ptrtype) \ { \ __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \ type buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \ } #define STRUCT_KFIFO(type, size) \ struct __STRUCT_KFIFO(type, size, 0, type) #define __STRUCT_KFIFO_PTR(type, recsize, ptrtype) \ { \ __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \ type buf[0]; \ } #define STRUCT_KFIFO_PTR(type) \ struct __STRUCT_KFIFO_PTR(type, 0, type) /* * helper macro to distinguish between real in place fifo where the fifo * array is a part of the structure and the fifo type where the array is * outside of the fifo structure. */ #define __is_kfifo_ptr(fifo) \ (sizeof(*fifo) == sizeof(STRUCT_KFIFO_PTR(typeof(*(fifo)->type)))) /** * DECLARE_KFIFO_PTR - macro to declare a fifo pointer object * @fifo: name of the declared fifo * @type: type of the fifo elements */ #define DECLARE_KFIFO_PTR(fifo, type) STRUCT_KFIFO_PTR(type) fifo /** * DECLARE_KFIFO - macro to declare a fifo object * @fifo: name of the declared fifo * @type: type of the fifo elements * @size: the number of elements in the fifo, this must be a power of 2 */ #define DECLARE_KFIFO(fifo, type, size) STRUCT_KFIFO(type, size) fifo /** * INIT_KFIFO - Initialize a fifo declared by DECLARE_KFIFO * @fifo: name of the declared fifo datatype */ #define INIT_KFIFO(fifo) \ (void)({ \ typeof(&(fifo)) __tmp = &(fifo); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ __kfifo->in = 0; \ __kfifo->out = 0; \ __kfifo->mask = __is_kfifo_ptr(__tmp) ? 0 : ARRAY_SIZE(__tmp->buf) - 1;\ __kfifo->esize = sizeof(*__tmp->buf); \ __kfifo->data = __is_kfifo_ptr(__tmp) ? NULL : __tmp->buf; \ }) /** * DEFINE_KFIFO - macro to define and initialize a fifo * @fifo: name of the declared fifo datatype * @type: type of the fifo elements * @size: the number of elements in the fifo, this must be a power of 2 * * Note: the macro can be used for global and local fifo data type variables. */ #define DEFINE_KFIFO(fifo, type, size) \ DECLARE_KFIFO(fifo, type, size) = \ (typeof(fifo)) { \ { \ { \ .in = 0, \ .out = 0, \ .mask = __is_kfifo_ptr(&(fifo)) ? \ 0 : \ ARRAY_SIZE((fifo).buf) - 1, \ .esize = sizeof(*(fifo).buf), \ .data = __is_kfifo_ptr(&(fifo)) ? \ NULL : \ (fifo).buf, \ } \ } \ }
参考https://elixir.bootlin.com/linux/v5.4.240/source/samples/kfifo/inttype-example.c#L40,有两种方式定义kfifo:
DYNAMIC:使用DECLARE_KFIFO_PTR定义kfifo 后,使用kfifo_alloc 初始化struct _kfifo, 并动态申请kfifo->data的memory
STATIC:使用DEFINE_KFIF定义kfifo.
#ifdef DYNAMIC static DECLARE_KFIFO_PTR(test, int); #else static DEFINE_KFIFO(test, int, FIFO_SIZE); #endif static int __init example_init(void) { #ifdef DYNAMIC int ret; ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL); if (ret) { printk(KERN_ERR "error kfifo_alloc\n"); return ret; } #endif if (testfunc() < 0) { #ifdef DYNAMIC kfifo_free(&test); #endif return -EIO; } if (proc_create(PROC_FIFO, 0, NULL, &fifo_fops) == NULL) { #ifdef DYNAMIC kfifo_free(&test); #endif return -ENOMEM; } return 0; } static void __exit example_exit(void) { remove_proc_entry(PROC_FIFO, NULL); #ifdef DYNAMIC kfifo_free(&test); #endif }