kfifo Introduction

**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 <linux/kfifo.h>

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();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值