• Linux内核kfifo


    一、kfifo原理

           kfifo实现原理是采用循环(环形)队列。

    struct kfifo
    {
        unsigned char *buffer; / *保存数据的缓冲区* /
        unsigned int size;      / *分配的缓冲区的大小* /
        unsigned int in;        / *数据以偏移量(in%size)添加* /
        unsigned int out;       / *数据从off中提取。(out%size)* /
    };

    二、kfifo特点

    1、采用环形缓冲区来实现,提供一个无边界的字节流服务。采用环形缓冲区的好处为,当一个数据元素被用掉后,其余数据元素不需要移动其存储位置,从而减少拷贝提高效率。

    2、保证缓冲区大小为2的次幂,不是的向上取整为2的次幂(很重要)

    3、使用无符号整数保存输入(in)和输出(out)的位置,在输入输出时不对in和out的值进行模运算,而让其自然溢出,并能够保证in-out的结果为缓冲区中已存放的数据长度。

    4、将需要取模的运算用 & 操作代替( a % size = (a & (size − 1)) ), 这需要size保证为2的次幂

    5、使用内存屏障(Memory Barrier)技术,实现单消费者和单生产者对kfifo的无锁并发访问(包括多CPU的情况),多个消费者、生产者的并发访问还是需要加锁的。

    关于kfifo中内存屏障的使用,请参考https://www.linuxidc.com/Linux/2016-12/137936.htm

    三、kfifo功能函数

    1、创建队列

          kfifo提供两种创建队列的方法,动态创建和静态创建。

    1)动态创建

    struct kfifo g_fifoqueue;

    int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask);

          该函数创建并初始化一个size大小的kfifo。内核使用gfp_mask标识符分配队列的缓冲区内存。如果成功,函数返回0,错误则返回负数的错误码。如果要自己分配缓冲区,可以调用函数:

    void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size);

    2)静态创建

    DECLARE_KFIFO(name, size) ;

    INIT_KFIFO(name);

    2、推入队列数据

          对于推入队列数据,kfifo提供三大类函数:常规函数, 将用户空间数据推入队列的函数,带记录域功能的函数。

    1)常规函数

    unsigned int kfifo_in(struct kfifo *fifo, const void *from, unsigned int len);

    unsigned int kfifo_in_locked(struct kfifo *fifo, const void *from, unsigned int n, spinlock_t *lock);

    2)将用户空间数据推入队列的函数

    int kfifo_from_user(struct kfifo *fifo, const void __user *from, unsigned int n, unsigned *lenout);

    unsigned int kfifo_from_user_rec(struct kfifo *fifo, const void __user *from, unsigned int n, unsigned int recsize);

    3)带记录域功能的函数

    unsigned int kfifo_in_rec(struct kfifo *fifo, void *from, unsigned int n, unsigned int recsize);             

    3、摘取队列数据      

          对于摘取队列数据,kfifo提供三大类函数:常规函数, 摘取队列数据至用户空间的函数,带记录域功能的函数。

    1)常规函数

    unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len);

    unsigned int kfifo_out_locked(struct kfifo *fifo, void *to, unsigned int n, spinlock_t *lock);

    unsigned int kfifo_out_peek(struct kfifo *fifo, void *to, unsigned int len, unsigned offset);

     2)摘取队列数据至用户空间的函数

    unsigned  int kfifo_to_user(struct kfifo *fifo, void __user *to, unsigned int n, unsigned *lenout);

    unsigned int kfifo_to_user_rec(struct kfifo *fifo, void __user *to, unsigned int n, unsigned int recsize, unsigned int *total);

     3)带记录域功能的函数

    unsigned int kfifo_out_rec(struct kfifo *fifo, void *to, unsigned int n, unsigned int recsize, unsigned int *total);

    4、获取队列长度

    1)获取队列缓冲区大小

    unsigned int kfifo_size(struct kfifo *fifo);

    2)获取队列已推入的数据大小

    unsigned int kfifo_len(struct kfifo *fifo);

    3)获取队列可用空间大小

    unsigned int kfifo_avail(struct kfifo *fifo);

    4)判断队列是否空

    int kfifo_is_empty(struct kfifo *fifo);

    5)判断队列是否满

    int kfifo_is_full(struct kfifo *fifo);

    5、重置和撤销队列 

    1)重置队列

    void kfifo_reset(struct kfifo *fifo);

    void kfifo_skip(struct kfifo *fifo, unsigned int len);

    2)撤销队列

    如果队列是由函数kfifo_alloc创建,则撤销队列使用:

    void kfifo_free(struct kfifo *fifo);

    如果队列是由函数kfifo_init创建,则你需要负责释放相关缓冲。  

    使用例子:

    struct fifo_st
    {
        char * addr;
        int len;
    }
    
    struct kfifo g_fifoqueue;
    ret = kfifo(&g_fifoqueue,FIFO_SIZE * sizeof(fifo_st),GFP_ATOMIC);//队列初始化
    
    if (sizeof(fifo_st) == kfifo_in(&g_fifoqueue,(unsigned char*)pstIn,(unsigned int)sizeof(fifo_st))) //入队列,判断语句。
    
    if (sizeof(fifo_st) == kfifo_out(&g_fifoqueue,(unsigned char*)pstOut,(unsigned int)sizeof(fifo_st)))//出队列,判断语句。

    源码:

    static inline int kfifo_initialized(struct kfifo *fifo)
    {
        return fifo->buffer != NULL;
    }
     
    /**
     * kfifo_reset - removes the entire FIFO contents
     * @fifo: the fifo to be emptied.
     */
    static inline void kfifo_reset(struct kfifo *fifo)
    {
        fifo->in = fifo->out = 0;
    }
     
    /**
     * kfifo_reset_out - skip FIFO contents
     * @fifo: the fifo to be emptied.
     */
    static inline void kfifo_reset_out(struct kfifo *fifo)
    {
        fifo->out = fifo->in;
    }
     
    /**
     * kfifo_size - returns the size of the fifo in bytes
     * @fifo: the fifo to be used.
     */
    static inline unsigned int kfifo_size(struct kfifo *fifo)
    {
        return fifo->size;
    }
     
    /**
     * kfifo_len - returns the number of used bytes in the FIFO
     * @fifo: the fifo to be used.
     */
    static inline unsigned int kfifo_len(struct kfifo *fifo)
    {
        register unsigned int    out;
     
        out = fifo->out;
        
        return fifo->in - out;
    }
     
    /**
     * kfifo_is_empty - returns true if the fifo is empty
     * @fifo: the fifo to be used.
     */
    static inline int kfifo_is_empty(struct kfifo *fifo)
    {
        return fifo->in == fifo->out;
    }
     
    /**
     * kfifo_is_full - returns true if the fifo is full
     * @fifo: the fifo to be used.
     */
    static inline int kfifo_is_full(struct kfifo *fifo)
    {
        return kfifo_len(fifo) == kfifo_size(fifo);
    }
     
    /**
     * kfifo_avail - returns the number of bytes available in the FIFO
     * @fifo: the fifo to be used.
     */
    static inline unsigned int kfifo_avail(struct kfifo *fifo)
    {
        return kfifo_size(fifo) - kfifo_len(fifo);
    }
     
    extern void kfifo_skip(struct kfifo *fifo, unsigned int len);
     
    /*
     * __kfifo_add_out internal helper function for updating the out offset
     */
    static inline void __kfifo_add_out(struct kfifo *fifo,
                    unsigned int off)
    {
        fifo->out += off;
    }
     
    /*
     * __kfifo_add_in internal helper function for updating the in offset
     */
    static inline void __kfifo_add_in(struct kfifo *fifo,
                    unsigned int off)
    {
        fifo->in += off;
    }
     
    /*
     * __kfifo_off internal helper function for calculating the index of a
     * given offeset
     */
    static inline unsigned int __kfifo_off(struct kfifo *fifo, unsigned int off)
    {
        return off & (fifo->size - 1);
    }
     
     
    unsigned int kfifo_in(struct kfifo *fifo, const void *from,
                    unsigned int len)
    {
        len = min(kfifo_avail(fifo), len);
     
        __kfifo_in_data(fifo, from, len, 0);
        __kfifo_add_in(fifo, len);
        return len;
    }
    
    unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len)
    {
        len = min(kfifo_len(fifo), len);
     
        __kfifo_out_data(fifo, to, len, 0);
        __kfifo_add_out(fifo, len);
     
        return len;
    }

     引用:

    https://blog.csdn.net/weixin_45228780/article/details/98937989?utm_medium=distribute.pc_relevant.none-task-blog-title-1&spm=1001.2101.3001.4242

    https://blog.csdn.net/fangye945a/article/details/86617551

  • 相关阅读:
    找水王
    环状二维数组最大子数组和
    用户模板
    课堂作业-电梯调度
    书店折扣问题
    《软件工程》读后感
    首尾相连的二维数组最大子数组求和
    梦断代码读后感(二)
    返回一个整数数组中最大子数组的和之测试
    首尾相连的一位数组最大子数组和
  • 原文地址:https://www.cnblogs.com/ggzhangxiaochao/p/13689741.html
Copyright © 2020-2023  润新知