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