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