**FIFO简介**
FIFO就是先进先出的意思,一般用队列表示,Linux Kernel实现了一个通用的FIFO, 称之为kfifo。
本文参照linux-kernel-3.19的代码,由于代码相对于老的接口有一些变动, 所以对于用户来说需要作出如下一些更变。
将类型声明由 struct kfifo * 变为 struct kfifo
使用 kfifo_alloc() 或 kfifo_init() 初始化
kfifo_in/kfifo_out 替代 __kfifo_put/__kfifo_get 表示免锁算法
kfifo_in_spinlocked/kfifo_out_spinlocked 替代 kfifo_put/kfifo_get 表示要加锁的算法
__kfifo_* 函数被更名为 kfifo_*
如果只有一个写入者,一个读取者,是不需要锁的。 对于多个写入者,一个读取者,只需要对写入者上锁。 反之,如果有多个读取者,一个写入者,只需要对读取者上锁。
**FIFO实现**
数据结构
#include
struct __kfifo {
unsigned int in;
unsigned int out;
unsigned int mask;
unsigned int esize;
void *data;
};
in/out & data 构成一个队列,in指向队列头,out指向队列尾, 如 in−out 就是队列长度。 esize 元素大小 mask 用于表示FIFO的总大小,也就是data的长度减1
但是到目前为止,还没有看到struct kfifo的定义,实际上它是由一个宏来定义的。
struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0, void);
#define __STRUCT_KFIFO_PTR(type, recsize, ptrtype) \
{ \
__STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
type buf[0]; \
}
type 实际就是队列元素的类型,kfifo以unsigned char作为元素类型 recsize 全称叫record size,很显然kfifo的record size为0. 因此下文不再讨论recsize不为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; \
}
这段代码设计非常精巧,我们知道kfifo中并不包含元素数据类型,元素指针类型, 那么如果利用kfifo来确定相关数据类型呢?共同体为我们保存了这些信息, 例如我们要想知道fifo的元素类型,就可以通过 typeof(*fifo->type) 获取。 也就是除了kfifo是直接用于存储数据以外,其它的字段是为了获取类型。
精巧的宏
DECLARE_KFIFO
kfifo的实现中运用了大量的宏,并且比较复杂,因此先找一个比较简单的切入口,逐步分析。
#define DECLARE_KFIFO(fifo, type, size) STRUCT_KFIFO(type, size) fifo
DECLARE_KFIFO 该宏用于静态声明一个kfifo对象 fifo 要定义的kfifo的名字 type 元素的类型 size kfifo可容纳的元素个数,必须是2的幂
#define STRUCT_KFIFO(type, size) \
struct __STRUCT_KFIFO(type, size, 0, type)
#define __STRUCT_KFIFO(type, size, recsize, ptrtype) \
{ \
__STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
type buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \
}
recsize 这里的recsize也被设置为0 buf 用于存放元素, size & (size - 1) 用于检测大小是否为2的幂
DECLARE_KFIFO只是声明一个变量,要定一个kfifo还需要必要的初始化, 一般情况使用DEFILE_KFIFO来声明并初始化一个kfifo。
#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, \
} \
} \
kfifo_alloc
静态分配的情况其实比较少见,更多的是动态的分配。
1: #define kfifo_alloc(fifo, size, gfp_mask) \
2: __kfifo_int_must_check_helper(({ \
3: typeof((fifo) + 1) __tmp = (fifo); \
4: struct __kfifo *__kfifo = &__tmp->kfifo; \
5: __is_kfifo_ptr(__tmp) ? \
6: __kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : \
7: -EINVAL; \
8: }))
9:
10: #define __is_kfifo_ptr(fifo) (sizeof(*fifo) == sizeof(struct __kfifo))
2 __kfifo_int_must_check_helper是一个辅助函数, 主要是为了帮助警告用户忘记检查返回值 3 typeof((fifo) + 1) 这里为什么要加1呢, 主要的好处是帮助确定传递的参数类型是否正确, 如果传递的是结构体会产生编译错误,如果传递的是数组名, 如 int fifo[4] ,typeof(fifo)的结果为 int [4] , 而typeof(fifo + 1)的结果为 int *
分配data
size/len/avail
#define kfifo_size(fifo) ((fifo)->kfifo.mask + 1)
#define kfifo_len(fifo) \
({ \
typeof((fifo) + 1) __tmpl = (fifo); \
__tmpl->kfifo.in - __tmpl->kfifo.out; \
})
#define kfifo_is_empty(fifo) \
({ \
typeof((fifo) + 1) __tmpq = (fifo); \
__tmpq->kfifo.in == __tmpq->kfifo.out; \
})
#define kfifo_is_full(fifo) \
({ \
typeof((fifo) + 1) __tmpq = (fifo); \
kfifo_len(__tmpq) > __tmpq->kfifo.mask; \
})
#define kfifo_avail(fifo) \
__kfifo_uint_must_check_helper(({ \
typeof((fifo) + 1) __tmpq = (fifo); \
const size_t __recsize = sizeof(*__tmpq->rectype); \
unsigned int __avail = kfifo_size(__tmpq) - kfifo_len(__tmpq); \
(__recsize) ? \
((__avail <= __recsize) ? 0 : \
__kfifo_max_r(__avail - __recsize, __recsize)) : \
__avail; \
}))
reset/init
#define kfifo_reset(fifo) \
(void)({ \
typeof((fifo) + 1) __tmp = (fifo); \
__tmp->kfifo.in = __tmp->kfifo.out = 0; \
})
#define kfifo_reset_out(fifo) \
(void)({ \
typeof((fifo) + 1) __tmp = (fifo); \
__tmp->kfifo.out = __tmp->kfifo.in; \
})
kfifo_in
#define kfifo_in(fifo, buf, n) \
({ \
typeof((fifo) + 1) __tmp = (fifo); \
typeof(__tmp->ptr_const) __buf = (buf); \
unsigned long __n = (n); \
const size_t __recsize = sizeof(*__tmp->rectype); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
(__recsize) ? \
__kfifo_in_r(__kfifo, __buf, __n, __recsize) : \
__kfifo_in(__kfifo, __buf, __n); \
})
#define kfifo_in_spinlocked(fifo, buf, n, lock) \
({ \
unsigned long __flags; \
unsigned int __ret; \
spin_lock_irqsave(lock, __flags); \
__ret = kfifo_in(fifo, buf, n); \
spin_unlock_irqrestore(lock, __flags); \
__ret; \
})
unsigned int __kfifo_in(struct __kfifo *fifo,
const void *buf, unsigned int len)
{
unsigned int l;
l = kfifo_unused(fifo);
if (len > l)
len = l;
kfifo_copy_in(fifo, buf, len, fifo->in);
fifo->in += len;
return len;
}
static inline unsigned int kfifo_unused(struct __kfifo *fifo)
{
return (fifo->mask + 1) - (fifo->in - fifo->out);
}
kfifo_unused 用于计算有多少个元素还没有使用, 这个函数清楚的显示了kfifo是一个循环队列。
static void kfifo_copy_in(struct __kfifo *fifo, const void *src,
unsigned int len, unsigned int off)
{
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
unsigned int l;
off &= fifo->mask;
if (esize != 1) {
off *= esize;
size *= esize;
len *= esize;
}
l = min(len, size - off);
memcpy(fifo->data + off, src, l);
memcpy(fifo->data, src + l, len - l);
/*
* make sure that the data in the fifo is up to date before
* incrementing the fifo->in index counter
*/
smp_wmb();
}
kfifo_out
#define kfifo_out(fifo, buf, n) \
__kfifo_uint_must_check_helper(({ \
typeof((fifo) + 1) __tmp = (fifo); \
typeof(__tmp->ptr) __buf = (buf); \
unsigned long __n = (n); \
const size_t __recsize = sizeof(*__tmp->rectype); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
(__recsize) ? \
__kfifo_out_r(__kfifo, __buf, __n, __recsize) : \
__kfifo_out(__kfifo, __buf, __n); \
}))
#define kfifo_out_spinlocked(fifo, buf, n, lock) \
__kfifo_uint_must_check_helper(({ \
unsigned long __flags; \
unsigned int __ret; \
spin_lock_irqsave(lock, __flags); \
__ret = kfifo_out(fifo, buf, n); \
spin_unlock_irqrestore(lock, __flags); \
__ret; \
}))
unsigned int __kfifo_out(struct __kfifo *fifo,
void *buf, unsigned int len)
{
len = __kfifo_out_peek(fifo, buf, len);
fifo->out += len;
return len;
}
unsigned int __kfifo_out_peek(struct __kfifo *fifo,
void *buf, unsigned int len)
{
unsigned int l;
l = fifo->in - fifo->out;
if (len > l)
len = l;
kfifo_copy_out(fifo, buf, len, fifo->out);
return len;
}
static void kfifo_copy_out(struct __kfifo *fifo, void *dst,
unsigned int len, unsigned int off)
{
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
unsigned int l;
off &= fifo->mask;
if (esize != 1) {
off *= esize;
size *= esize;
len *= esize;
}
l = min(len, size - off);
memcpy(dst, fifo->data + off, l);
memcpy(dst + l, fifo->data, len - l);
/*
* make sure that the data is copied before
* incrementing the fifo->out index counter
*/
smp_wmb();
}