一、提供的接口
#
1. 定义相关
#
1// include/linux/kfifo.h
2/**
3 * DECLARE_KFIFO - macro to declare a fifo object
4 * @fifo: name of the declared fifo
5 * @type: type of the fifo elements
6 * @size: the number of elements in the fifo, this must be a power of 2
7 */
8#define DECLARE_KFIFO(fifo, type, size) STRUCT_KFIFO(type, size) fifo
9
10/**
11 * INIT_KFIFO - Initialize a fifo declared by DECLARE_KFIFO
12 * @fifo: name of the declared fifo datatype
13 */
14#define INIT_KFIFO(fifo) \
15(void)({ \
16 typeof(&(fifo)) __tmp = &(fifo); \
17 struct __kfifo *__kfifo = &__tmp->kfifo; \
18 __kfifo->in = 0; \
19 __kfifo->out = 0; \
20 __kfifo->mask = __is_kfifo_ptr(__tmp) ? 0 : ARRAY_SIZE(__tmp->buf) - 1;\
21 __kfifo->esize = sizeof(*__tmp->buf); \
22 __kfifo->data = __is_kfifo_ptr(__tmp) ? NULL : __tmp->buf; \
23})
1// include/linux/kfifo.h
2/**
3 * DEFINE_KFIFO - macro to define and initialize a fifo
4 * @fifo: name of the declared fifo datatype
5 * @type: type of the fifo elements
6 * @size: the number of elements in the fifo, this must be a power of 2
7 *
8 * Note: the macro can be used for global and local fifo data type variables.
9 */
10#define DEFINE_KFIFO(fifo, type, size) \
11 DECLARE_KFIFO(fifo, type, size) = \
12 (typeof(fifo)) { \
13 { \
14 { \
15 .in = 0, \
16 .out = 0, \
17 .mask = __is_kfifo_ptr(&(fifo)) ? \
18 0 : \
19 ARRAY_SIZE((fifo).buf) - 1, \
20 .esize = sizeof(*(fifo).buf), \
21 .data = __is_kfifo_ptr(&(fifo)) ? \
22 NULL : \
23 (fifo).buf, \
24 } \
25 } \
26 }
2. 操作相关
#
1// include/linux/kfifo.h
2/**
3 * kfifo_put - put data into the fifo
4 * @fifo: address of the fifo to be used
5 * @val: the data to be added
6 *
7 * This macro copies the given value into the fifo.
8 * It returns 0 if the fifo was full. Otherwise it returns the number
9 * processed elements.
10 *
11 * Note that with only one concurrent reader and one concurrent
12 * writer, you don't need extra locking to use these macro.
13 */
14#define kfifo_put(fifo, val)
15...
16
17/**
18 * kfifo_get - get data from the fifo
19 * @fifo: address of the fifo to be used
20 * @val: address where to store the data
21 *
22 * This macro reads the data from the fifo.
23 * It returns 0 if the fifo was empty. Otherwise it returns the number
24 * processed elements.
25 *
26 * Note that with only one concurrent reader and one concurrent
27 * writer, you don't need extra locking to use these macro.
28 */
29#define kfifo_get(fifo, val) \
30__kfifo_uint_must_check_helper( \
31({ \
32 typeof((fifo) + 1) __tmp = (fifo); \
33 typeof(__tmp->ptr) __val = (val); \
34 unsigned int __ret; \
35 const size_t __recsize = sizeof(*__tmp->rectype); \
36 struct __kfifo *__kfifo = &__tmp->kfifo; \
37 if (__recsize) \
38 __ret = __kfifo_out_r(__kfifo, __val, sizeof(*__val), \
39 __recsize); \
40 else { \
41 __ret = !kfifo_is_empty(__tmp); \
42 if (__ret) { \
43 *(typeof(__tmp->type))__val = \
44 (__is_kfifo_ptr(__tmp) ? \
45 ((typeof(__tmp->type))__kfifo->data) : \
46 (__tmp->buf) \
47 )[__kfifo->out & __tmp->kfifo.mask]; \
48 smp_wmb(); \
49 __kfifo->out++; \
50 } \
51 } \
52 __ret; \
53}) \
54)
3. 获取属性
#
1// include/linux/kfifo.h
2/**
3 * kfifo_is_empty - returns true if the fifo is empty
4 * @fifo: address of the fifo to be used
5 */
6#define kfifo_is_empty(fifo) \
7({ \
8 typeof((fifo) + 1) __tmpq = (fifo); \
9 __tmpq->kfifo.in == __tmpq->kfifo.out; \
10})
11
12/**
13 * kfifo_is_full - returns true if the fifo is full
14 * @fifo: address of the fifo to be used
15 */
16#define kfifo_is_full(fifo)
示例用法
#
1/* keybuf.h */
2#include <linux/kfifo.h>
3#define KEYBUF_KFIFO_SIZE 32
4// 这里将类型单独定义出来
5typedef STRUCT_KFIFO(unsigned char, 32) KeyBufType;
6// 按键结构体
7extern KeyBufType g_keybuf;
8
9/* keybuf.c */
10#include <linux/kfifo.h>
11KeyBufType g_keybuf = (typeof(g_keybuf)){{{
12 .in = 0,
13 .out = 0,
14 .mask = __is_kfifo_ptr(&(g_keybuf)) ? 0 : ARRAY_SIZE((g_keybuf).buf) - 1,
15 .esize = sizeof(*(g_keybuf).buf),
16 .data = __is_kfifo_ptr(&(g_keybuf)) ? NULL : (g_keybuf).buf,
17}}};
18
19void intHandler() {
20 ...
21 // 插入一个数据
22 kfifo_put(&g_keybuf, data);
23}
24
25/* main.c */
26#include "keybuf.h"
27
28int main() {
29 for (;;) {
30 io_cli();
31 // 判断是否为空
32 if (kfifo_is_empty(&g_keybuf)) {
33 io_stihlt();
34 } else {
35 // 取一个数据出来
36 unsigned char i;
37 kfifo_get(&g_keybuf, &i);
38 ...
39 }
40 }
41}
二、源码分析
#
1. 定义
#
1// include/linux/kfifo.h
2struct __kfifo {
3 unsigned int in; // 写索引
4 unsigned int out; // 读索引
5 unsigned int mask; // 读写的mask,循环的高效写法的关键
6 unsigned int esize; // 数据的长度
7 void *data; // 数据段起始地址
8};
- 联合声明
type
、const_type
、ptr
和ptr_const
的值没有实际含义,只是在后续实现的时候可以使用typeof()
来找到类型,可以理解成是模板的C语言版本实现
rectype
是作为一个占位符存在的,看清楚是一个指针数组,相当于可以指定头部大小,recsize指定即可,默认的是0,也就是按照__kfifo
的大小最小占位
1#define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \
2 union { \
3 struct __kfifo kfifo; \
4 datatype *type; \
5 const datatype *const_type; \
6 char (*rectype)[recsize]; \
7 ptrtype *ptr; \
8 ptrtype const *ptr_const; \
9 }
2. 操作
#
2.1. kfifo_put
#
1// include/linux/kfifo.h
2#define kfifo_put(fifo, val) \
3({ \
4 typeof((fifo) + 1) __tmp = (fifo); \
5 typeof(*__tmp->const_type) __val = (val); \
6 unsigned int __ret; \
7 size_t __recsize = sizeof(*__tmp->rectype); \
8 struct __kfifo *__kfifo = &__tmp->kfifo; \
9 if (__recsize) \
10 __ret = __kfifo_in_r(__kfifo, &__val, sizeof(__val), \
11 __recsize); \
12 else { \
13 __ret = !kfifo_is_full(__tmp); \
14 if (__ret) { \
15 (__is_kfifo_ptr(__tmp) ? \
16 ((typeof(__tmp->type))__kfifo->data) : \
17 (__tmp->buf) \
18 )[__kfifo->in & __tmp->kfifo.mask] = \
19 *(typeof(__tmp->type))&__val; \
20 smp_wmb(); \
21 __kfifo->in++; \
22 } \
23 } \
24 __ret; \
25})
1({
2 typeof((&fifo) + 1) __tmp = (&fifo);
3 typeof(*__tmp->const_type) __val = (data);
4 unsigned int __ret;
5 size_t __recsize = sizeof(*__tmp->rectype);
6 struct __kfifo *__kfifo = &__tmp->kfifo;
7 if (__recsize)
8 __ret = __kfifo_in_r(__kfifo, &__val, sizeof(__val), __recsize);
9 else {
10 __ret = !kfifo_is_full(__tmp);
11 if (__ret) {
12 (__is_kfifo_ptr(__tmp) ? ((typeof(__tmp->type))__kfifo->data)
13 : (__tmp->buf))[__kfifo->in & __tmp->kfifo.mask] = *(typeof(__tmp->type))&__val;
14 smp_wmb();
15 __kfifo->in++;
16 }
17 }
18 __ret;
19})
- 上面关键点在于
[__kfifo->in & __tmp->kfifo.mask]
,里面的in可以任意溢出,只保留mask的值
- 也就是假设size为32,mask为
0b11111
,in随便加,溢出后被mask与就只剩0-31
,也就是从0开始,形成循环
3. 属性
#
3.1. kfifo_is_full
#
1// include/linux/kfifo.h
2#define kfifo_is_full(fifo) \
3 ({ \
4 typeof((fifo) + 1) __tmpq = (fifo); \
5 kfifo_len(__tmpq) > __tmpq->kfifo.mask; \
6 })
1({
2 typeof((&fifo) + 1) __tmpq = (&fifo);
3 ({
4 typeof((__tmpq) + 1) __tmpl = (__tmpq);
5 __tmpl->kfifo.in - __tmpl->kfifo.out;
6 }) > __tmpq->kfifo.mask;
7})
- 直接用in减去out,肯定会想如果in溢出从0开始怎么办,举个例子就知道了。假设in和out都是8位,那么
0x00 - 0xff = 0x01
,可以看到,溢出不影响计算两个的差值,所以整个kfifo的操作过程中,in和out只管尽情加就好了,不用考虑溢出的问题
- 两个差值大于mask就说明确实满了,假设容量2个,差值为2,mask为1,大于正好满了