转载:linux kfifo

发布时间 2023-04-09 23:31:50作者: fellow_jing

参考: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
}