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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM