一、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