Linux内核kfifo

基于 msm-5.4


一、kfifo简介

Linux 内核通用队列实现称为 kfifo。它实现在文件 kerel/kfifo.c 中,声明在文件 <linux/kfifo.h> 中。

Linux 的 kfifo 和多数其他队列实现类似,提供了两个主要操作: enqueue(入队列) 和 dequeue (出队列)。kfifo 对象维护了两个偏移量: 入口偏移和出口偏移。
入口偏移是指下一次入队列时的位置,出口偏移是指下一次出队列时的位置。出口偏移总是小于等于入口偏移,否则无意义,因为那样说明要出队列的元素根本还没有入队列。
enqueue 操作拷贝数据到队列中的入口偏移位置。当上述动作完成后,入口偏移随之加上推入的元素数目。
dequeue 操作从队列中出口偏移处拷贝数据,当上述动作完成后,出口偏移随之减去摘取的元素数目。
当出口偏移等于入口偏移时,说明队列空了,在新数据被推入前,不可再摘取任何数据了。当入口偏移等于队列长度时,说明在队列重置前,不可再有新数据推入队列。


1. 实现文件

//msm-5.4/include/linux/kfifo.h
//msm-5.4/lib/kfifo.c


2. 使用Demo

内核中有三个例子:

//msm-5.4/samples/kfifo/Makefile
obj-$(CONFIG_SAMPLE_KFIFO) += bytestream-example.o dma-example.o inttype-example.o record-example.o

msm-5.4/samples/kfifo/bytestream-example.c
msm-5.4/samples/kfifo/dma-example.c
msm-5.4/samples/kfifo/inttype-example.c
msm-5.4/samples/kfifo/record-example.c

<pj_name>_userdebug.config 中加入下面配置,然后 make bootimage, 使用 dlkm/lib/modules 下编译出来的ko.
CONFIG_SAMPLES=y
CONFIG_SAMPLE_KFIFO=m

注: inttype-example.c 中有BUG, echo 1 > /proc/int-fifo 会卡成死循环,应该是 kfifo_from_user() 中的 count 是 buf 中字符的个数,而不是整型元素的个数有关。


二、数据结构

1. struct __kfifo

struct __kfifo { //kfifo.h
    unsigned int    in;
    unsigned int    out;
    unsigned int    mask;
    unsigned int    esize;
    void        *data;
};


2. struct kfifo

struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0, void);
/* 展开得到: */
struct kfifo { //kfifo.h
    union {
        struct __kfifo kfifo;
        unsigned char *type;
        const unsigned char *const_type;
        char (*rectype)[0];
        void *ptr;
        void const *ptr_const;
    };
    unsigned char buf[0];
};


3. struct kfifo_rec_ptr_1

struct kfifo_rec_ptr_1 __STRUCT_KFIFO_PTR(unsigned char, 1, void);
/* 展开得到: */
struct kfifo_rec_ptr_1 { //kfifo.h
    union {
        struct __kfifo kfifo;
        unsigned char *type;
        const unsigned char *const_type;
        char (*rectype)[1];
        void *ptr;
        void const *ptr_const;
    };
    unsigned char buf[0];
};


4. struct kfifo_rec_ptr_2

struct kfifo_rec_ptr_2 __STRUCT_KFIFO_PTR(unsigned char, 2, void);
/* 展开得到: */
struct kfifo_rec_ptr_2 {
    union {
        struct __kfifo kfifo;
        unsigned char *type;
        const unsigned char *const_type;
        char (*rectype)[2];
        void *ptr;
        void const *ptr_const;
    };
    unsigned char buf[0];
};


二、kfifo用法

1. kfifo静态定义

#define DECLARE_KFIFO_PTR(fifo, type)    STRUCT_KFIFO_PTR(type) fifo
/* DECLARE_KFIFO_PTR(test_fifo, unsigned char); 展开后: */
struct {
    union {
        struct __kfifo kfifo;
        unsigned char *type; 
        const unsigned char *const_type; 
        char (*rectype)[0]; 
        unsigned char *ptr; 
        unsigned char const *ptr_const; 
    };
    unsigned char buf[0];
} test_fifo;


#define DECLARE_KFIFO(fifo, type, size)    STRUCT_KFIFO(type, size) fifo
/* DECLARE_KFIFO(test_fifo, unsigned char, FIFO_SIZE); 展开后如下 */
struct {
    union {
        struct __kfifo kfifo;
        unsigned char *type;
        const unsigned char *const_type;
        char (*rectype)[0];
        unsigned char *ptr;
        unsigned char const *ptr_const;
    };
    unsigned char buf[((FIFO_SIZE < 2) || (FIFO_SIZE & (FIFO_SIZE - 1))) ? -1 : FIFO_SIZE];
} test_fifo;


2. 初始化

#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; \
})

__is_kfifo_ptr 展开后如下:

#define    __is_kfifo_ptr(fifo) \
    (sizeof(*fifo) == sizeof(STRUCT_KFIFO_PTR(typeof(*(fifo)->type))))
/* __is_kfifo_ptr(my_fifo) 展开后为: */
(sizeof(*my_fifo) == sizeof (
        struct {
            union {
                struct __kfifo kfifo;
                typeof(*(my_fifo)->type) *type;
                const typeof(*(my_fifo)->type) *const_type; 
                char (*rectype)[0]; 
                typeof(*(my_fifo)->type) *ptr; 
                typeof(*(my_fifo)->type) const *ptr_const; 
            };
            typeof(*(my_fifo)->type) buf[0];
        }
    )
)
/*
对照下面实验展开就是:
(sizeof(*&my_2_fifo) == sizeof(
        struct {
            union {
                struct __kfifo kfifo;
                struct student *type;
                const struct student *const_type;
                char (*rectype)[0];
                struct student *ptr;
                struct student const *ptr_const;
            };
            struct student buf[0]; //这个成员不匹配,明显是假
        }
    )
)
*/

只是比较大小。应该是用来判断上面是 DECLARE_KFIFO_PTR() 还是 DECLARE_KFIFO() 定义的 kfifo。

实验一下:

struct student {
    char id;
    int score;
};

DECLARE_KFIFO_PTR(my_1_fifo, struct student);
/*
展开:
struct {
    union {
        struct __kfifo kfifo; //其实应该是只用这个成员,其他成员用于在编译预处理期帮助确定类型 ####
        struct student *type;
        const struct student *const_type;
        char (*rectype)[0];
        struct student *ptr;
        struct student const *ptr_const;
    };
    struct student buf[0];
} my_1_fifo;

*/

DECLARE_KFIFO(my_2_fifo, struct student, 64);
/* 展开: 
struct {
    union {
        struct __kfifo kfifo;
        struct student *type;
        const struct student *const_type;
        char (*rectype)[0];
        struct student *ptr;
        struct student const *ptr_const;
    };
    struct student buf[((64 < 2) || (64 & (64 - 1))) ? -1 : 64]; //size必须得是2^n,且n必须大于或等于1
} my_2_fifo;

size 是fifo中要存储 type 类型的元素的个数,而不是fifo的字节数,size 必须得是2^n,且n必须大于或等于1的。否则就给你编译报错*/

#include <stdio.h>

int main()
{
    if (__is_kfifo_ptr(&my_1_fifo)) {
        printf("my_1_fifo is ptr\n"); //这个打印了
    }
    if (__is_kfifo_ptr(&my_2_fifo)) {
        printf("my_2_fifo is ptr\n"); //这个没打印
    }
}

也就是判断是否是 ptr 就是看其是否使用的是结构体内部的 buf[] 成员是否为0长度数组,是则认为是指针,都在认为不是。


3. 创建队列

(1) 动态创建

static struct kfifo my_dyn_fifo; //全局变量
/* 创建 */
kfifo_alloc(&my_dyn_fifo, FIFO_SIZE, GFP_KERNEL);
/* 使用完后释放 */
kfifo_free(&my_dyn_fifo);

或:

static DECLARE_KFIFO_PTR(my_dyn_fifo, int);
kfifo_alloc(&my_dyn_fifo, FIFO_SIZE, GFP_KERNEL);
/* 使用完后 */
kfifo_free(&test);

(2) 静态创建

static DECLARE_KFIFO(my_sta_fifo, unsigned char, FIFO_SIZE); //创建的存储的数据类型是char的fifo
INIT_KFIFO(test);
/* 上面两行等效于 */
DEFINE_KFIFO(fifo, type, size); //定义一个能存放 size 个 type 类型变量的fifo.


4. 向fifo中存入数据

/* 从buf中向fifo中拷贝存入n个元素 */
kfifo_in(my_fifo, buf, n) //如存char类型数据 kfifo_in(&test, "hello", 5)
/* 向fifo中拷贝存入1个元素val */
fifo_put(my_fifo, val) //如存char类型数据 kfifo_put(&test, 'a')


5. 从fifo中读出数据

/* 从fifo中读取5个数据到buf中,返回实际读取个数 */
i = kfifo_out(&my_fifo, buf, 5); //printk("buf: %.*s\n", i, buf) 打印实际读取的字符

/* 从fifo中读取出一个数据,成功取出返回非0 */
kfifo_get(fifo, val) //如 kfifo_get(&test, &i)


6. 跳过数据

/* 跳过fifo中的第一个数据 */
kfifo_skip(&my_fifo)


7. fifo的长度

/* fifo中元素的个数 */
kfifo_len(&my_fifo);
/* fifo中元素存储空间的大小 */
kfifo_size(&my_fifo)


8. 窥探fifo中数据

/* 将读端首个元素存入变量i中 */
kfifo_peek(&test, &i)

 

posted on 2026-02-06 20:57  Hello-World3  阅读(0)  评论(0)    收藏  举报

导航