kfifo实现了环形缓冲区(RingBuffer),提供了无锁的单生产/单消费模式的共享队列;也就是如果只有一个写入者,一个读取者,是不需要锁的。 对于多个写入者,一个读取者,只需要对写入者上锁。 反之,如果有多个读取者,一个写入者,只需要对读取者上锁。在老的内核中直接定义了kfifo结构体,在新的内核中通过了一些稍显复杂的宏进行间接定义。我们先看下kfifo的原型__kfifo :
struct __kfifo {
//data is added at offset (in % size) --> 指向队列头
unsigned int in;
//data is extracted from off. (out % size) --> 指向队列尾
unsigned int out;
//用于表示FIFO的总大小,也就是data的长度减1 --> 在初化时,将它向上扩展成2的幂
unsigned int mask;
//the size of the allocated buffer --> 元素大小
unsigned int esize;
//the buffer holding the data --> 存储数据的缓冲区
void *data;
};
接着看我们如何静态定义一个kfifo对象:
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#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_KFIFO
* 函数功能 : 静态声明一个kfifo对象
* 输入参数 : fifo 要定义的kfifo的名字
type 元素类型
size 可容纳元素的个数,必须是2的幂
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 记 录
* 1.日 期: 2018年03月04日
* 修改内容: 新生成函数
*****************************************************************************/
#define DECLARE_KFIFO(fifo, type, size) STRUCT_KFIFO(type, size) fifo
/*****************************************************************************
* 函 数 名 : DEFINE_KFIFO
* 函数功能 : 静态声明并初始化一个kfifo对象
* 输入参数 : fifo 要定义的kfifo的名字
type 元素类型
size 可容纳元素的个数,必须是2的幂
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 记 录
* 1.日 期: 2018年03月04日
* 修改内容: 新生成函数
*****************************************************************************/
#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, \
} \
} \
}
看起来稍显复杂,单其实就是通过__STRUCT_KFIFO(type, size, recsize, ptrtype)定义了两部分一部分是环形缓冲区描述符——struct __kfifo结构对象,一部分是缓冲区本身——数组buf,然后初始化。宏展开之后是:
#define __STRUCT_KFIFO(type, size, recsize, ptrtype) \
union { \
struct __kfifo kfifo; \
datatype *type; \
const datatype *const_type; \
char (*rectype)[recsize]; \
ptrtype *ptr; \
ptrtype const *ptr_const; \
}
type buf[((size < 2) || (size & (size - 1))) ? -1 : size];
这里有一个联合体union ,除了描述符struct __kfifo kfifo,其他都是指针,这是为了方便获取元素类型(通过typeof);size & (size - 1)是为了确定缓冲区大小是2的幂,因为如果是2的幂,则相与结果为0。一般情况下我们不会静态声明,而是动态声明;我们看下如何动态声明:
/*****************************************************************************
* 函 数 名 : __STRUCT_KFIFO_PTR
* 函数功能 : kfifo结构体定义辅助宏
* 输入参数 : type 元素类型
recsize ?
ptrtype 指针类型
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 记 录
* 1.日 期: 2018年03月05日
* 作 者:
* 修改内容: 新生成函数
*****************************************************************************/
#define __STRUCT_KFIFO_PTR(type, recsize, ptrtype) \
{ \
__STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
type buf[0]; \
}
//kfifo以unsigned char作为元素类型
//struct kfifo的定义,实际上它是由一个宏来定义的。
struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0, void);
可以看到也借助到了__STRUCT_KFIFO_COMMON宏,但是元素类型直接被定义unsigned char,指针类型被指定为void,还看到缓冲区描述符中常见0长度数组buf(以便可以将方便缓冲区的分配和释放)。看下如何分配缓冲区实体:
static inline unsigned int __must_check
__kfifo_uint_must_check_helper(unsigned int val)
{
return val;
}
static inline int __must_check
__kfifo_int_must_check_helper(int val)
{
return val;
}
/*****************************************************************************
* 函 数 名 : kfifo_alloc
* 函数功能 : 动态分配一个环形缓冲区实体
* 输入参数 : fifo kfifo指针
size 缓冲区容量,必须是2的幂
gfp_mask 内存分配掩码
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 记 录
* 1.日 期: 2018年03月05日
* 作 者:
* 修改内容: 新生成函数
*****************************************************************************/
#define kfifo_alloc(fifo, size, gfp_mask) \
__kfifo_int_must_check_helper( \
({ \
typeof((fifo) + 1) __tmp = (fifo); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
__is_kfifo_ptr(__tmp) ? \
__kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : \
-EINVAL; \
}) \
)
1 __kfifo_int_must_check_helper是一个辅助函数, 主要是为了帮助警告用户忘记检查返回值
2 fifo是一个指针,就是__STRUCT_KFIFO中定义的联合体指针
3 typeof((fifo) + 1)这里为什么要加1呢, 主要的好处是帮助确定传递的参数类型是否正确(确保是指针), 如果传递的是结构体会产生编译错误,如果传递的是数组名, 如 int fifo[4] ,typeof(fifo)的结果为 int [4] , 而typeof(fifo + 1)的结果为 int *
4 实质借助的是__kfifo_alloc
我们看下__kfifo_alloc函数:
int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
size_t esize, gfp_t gfp_mask)
{
/*
* round down to the next power of 2, since our 'let the indices
* wrap' technique works only in this case.
*/
//将之向上扩展为2的幂
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;
}
fifo->data = kmalloc(size * esize, gfp_mask);
if (!fifo->data) {
fifo->mask = 0;
return -ENOMEM;
}
fifo->mask = size - 1;
return 0;
}
很简单,主要是分配一块缓冲区,然后将地址复制给struct __kfifo的data成员。还有一个要留意的是size被向上扩展为2的幂。这里要注意一点:
fifo->mask = size - 1;
描述符结构里的
mask并不是缓冲区的大小,而是缓冲区大小减一,也就是指示缓冲区满了,其实是有一个数据空间的。
再看下释放缓冲区:
/*****************************************************************************
* 函 数 名 : kfifo_free
* 函数功能 : 释放缓冲区内存
* 输入参数 : fifo kfifo指针
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 记 录
* 1.日 期: 2018年03月05日
* 作 者:
* 修改内容: 新生成函数
*****************************************************************************/
#define kfifo_free(fifo) \
({ \
typeof((fifo) + 1) __tmp = (fifo); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
if (__is_kfifo_ptr(__tmp)) \
__kfifo_free(__kfifo); \
})
void __kfifo_free(struct __kfifo *fifo)
{
kfree(fifo->data);
fifo->in = 0;
fifo->out = 0;
fifo->esize = 0;
fifo->data = NULL;
fifo->mask = 0;
}
可以看到并没有释放缓冲区描述符,而只是释放了缓冲区。
下面我们看下是怎么添加数据到缓冲区的:
/*****************************************************************************
* 函 数 名 : kfifo_put
* 函数功能 : 添加数据到环形缓冲区
* 输入参数 : fifo 环形缓冲区描述符指针
val 待添加的数据
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 记 录
* 1.日 期: 2018年05月06日
* 作 者:
* 修改内容: 新生成函数
*****************************************************************************/
#define kfifo_put(fifo, val) \
({ \
typeof((fifo) + 1) __tmp = (fifo); \
typeof(*__tmp->const_type) __val = (val); \
unsigned int __ret; \
size_t __recsize = sizeof(*__tmp->rectype); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
if (__recsize) \
__ret = __kfifo_in_r(__kfifo, &__val, sizeof(__val), \
__recsize); \
else { \
__ret = !kfifo_is_full(__tmp); \
if (__ret) { \
(__is_kfifo_ptr(__tmp) ? \
((typeof(__tmp->type))__kfifo->data) : \
(__tmp->buf) \
)[__kfifo->in & __tmp->kfifo.mask] = \
*(typeof(__tmp->type))&__val; \
smp_wmb(); \
__kfifo->in++; \
} \
} \
__ret; \
__recsize不是很懂我们不看,我们直接看else部分。
首选判断缓冲区是否满了,方法就是插入的数据大小减去取出的数据大小是否大于缓冲区大小:
/**
* kfifo_len - returns the number of used elements in the fifo
* @fifo: address of the fifo to be used
*/
#define kfifo_len(fifo) \
({ \
typeof((fifo) + 1) __tmpl = (fifo); \
__tmpl->kfifo.in - __tmpl->kfifo.out; \
})
/**
* kfifo_is_full - returns true if the fifo is full
* @fifo: address of the fifo to be used
*/
#define kfifo_is_full(fifo) \
({ \
typeof((fifo) + 1) __tmpq = (fifo); \
kfifo_len(__tmpq) > __tmpq->kfifo.mask; \
})
接着会判断是动态分配的,还是静态分配的;区别是动态分配的,描述符大小不包含缓冲区实体大小,静态分配的包含缓冲区实体大小(静态分配的描述符结构和数组作为一个整体结构体声明),因为动态分配的会将缓冲区实体地址赋值给描述符的data成员:
/*
* 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))
接着就是将数据插入缓冲区,指示插入数据大小的成员自加
__kfifo->in++;