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++;