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) 收藏 举报
浙公网安备 33010602011771号