linux內核kfifo

===============================   博客點滴積累,部分話語和知識點來源於網絡,感謝網絡資源的提供者======
kfifo 巧妙在:1)buf大小爲2**n  大小將(in % size)  轉化爲&運算:(fifo->in & (fifo->size - 1),提高運算效率,2)in,out 爲無符號整數,巧妙地利用溢出規則,保證了fifo讀寫正確,相比我們的普通方法減少很多的判斷處理。
內核的循環隊列結構體如下:
struct kfifo {
    unsigned char *buffer;    /* the buffer holding the data */
    unsigned int size;    /* the size of the allocated buffer */
    unsigned int in;    /* data is added at offset (in % size) */
    unsigned int out;    /* data is extracted from off. (out % size) */
    spinlock_t *lock;    /* protects concurrent modifications */
};

struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
             gfp_t gfp_mask, spinlock_t *lock)
{
    struct kfifo *fifo;
    BUG_ON(!is_power_of_2(size));    /* size must be a power of 2  實現方式如下,目的在於將來的(in % size)  轉化爲&運算:(fifo->in & (fifo->size - 1)*/  

    fifo = kmalloc(sizeof(struct kfifo), gfp_mask);
    if (!fifo)
        return ERR_PTR(-ENOMEM);

    fifo->buffer = buffer;
    fifo->size = size;
    fifo->in = fifo->out = 0;
    fifo->lock = lock;

    return fifo;
}
/*如果數爲2**n 那麼最高爲1,其餘bit 位爲0,所以n & (n - 1) 可以判斷是否是2**n */
static inline __attribute__((const))
bool is_power_of_2(unsigned long n)
{
    return (n != 0 && ((n & (n - 1)) == 0));  
}

struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock)
{
    unsigned char *buffer;
    struct kfifo *ret;

    /*
     * round up to the next power of 2, since our 'let the indices
     * wrap' tachnique works only in this case.
     */
    if (size & (size - 1)) {
        BUG_ON(size > 0x80000000);
        size = roundup_pow_of_two(size); /*宏定義roundup_pow_of_two,作用是:計算出最接近2的n次方並且大於size的值即向上擴展爲2整數次冪*/
    }

    buffer = kmalloc(size, gfp_mask);
    if (!buffer)
        return ERR_PTR(-ENOMEM);

    ret = kfifo_init(buffer, size, gfp_mask, lock);

    if (IS_ERR(ret))
        kfree(buffer);

    return ret;
}

#define roundup_pow_of_two(n)            \
(                        \
    __builtin_constant_p(n) ? (        \
        (n == 1) ? 1 :            \
        (1UL << (ilog2((n) - 1) + 1))    \
                   ) :        \
    __roundup_pow_of_two(n)            \
 )
/*向循環隊列寫入數據,希望寫入的數據長度爲len,函數返回寫入的實際長度*/
unsigned int __kfifo_put(struct kfifo *fifo, unsigned char *buffer, unsigned int len)
{
    unsigned int l;
    /*希望寫入的長度和fifo 剩餘的空間比較,看實際可以寫入多少數據  爲什麼 fifo->size - fifo->in + fifo->out 表示fifo的剩餘空間呢?fifo->size 可能小於 fifo->in 
     fifo->size - fifo->in + fifo->out  fifo->size - (fifo->in - fifo->out) 等價*/
    len = min(len, fifo->size - fifo->in + fifo->out);
    /*
     * Ensure that we sample the fifo->out index -before- we
     * start putting bytes into the kfifo.
     */
    smp_mb();

    /* first put the data starting from fifo->in to buffer end */
    /* fifo->size  爲2的n次方,fifo->in % fifo->size   等價於(fifo->in & (fifo->size - 1))   fifo->size - (fifo->in & (fifo->size - 1) 即表示fifo->in到fifo結尾還可以寫入的數據*/
    l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
    memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
    /* then put the rest (if any) at the beginning of the buffer */
    memcpy(fifo->buffer, buffer + l, len - l); /
    /*
     * Ensure that we add the bytes to the kfifo -before-
     * we update the fifo->in index.
     */
    smp_wmb();

    fifo->in += len; /*注意這裏fifo->in += len  並沒有求餘, fifo->in的大小可能比fifo->size 大,還可能溢出額*/

    return len;
}

/*從循環隊列讀出數據,希望讀出的數據長度爲len,函數返回的實際讀取的長度*/
unsigned int __kfifo_get(struct kfifo *fifo, unsigned char *buffer, unsigned int len)
{
    unsigned int l;
    len = min(len, fifo->in - fifo->out);
    /*
     * Ensure that we sample the fifo->in index -before- we
     * start removing bytes from the kfifo.
     */
    smp_rmb();
    /* first get the data from fifo->out until the end of the buffer */
    l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
    memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);

    /* then get the rest (if any) from the beginning of the buffer */
    memcpy(buffer + l, fifo->buffer, len - l);

    /*
     * Ensure that we remove the bytes from the kfifo -before-
     * we update the fifo->out index.
     */
    smp_mb();
    fifo->out += len;
    return len;
}
static inline unsigned int __kfifo_len(struct kfifo *fifo)
{
    return fifo->in - fifo->out;
}

主要想明白任何情況下,fifo->in - fifo->out 都表示fifo已經存入的數據大小,就很明白了,分情況說明一下
1) fifo->out < fifo->in    fifo->size      顯然len = fifo->in - fifo->out
2)   fifo->out  fifo->size < fifo->in        此時,fifo->size < fifo->in < 2 * fifo->size  
            len = (fifo->in % fifo->size - 0) + (fifo->size - fifo->out) =  fifo->in -  0 - fifo->out = fifo->in - fifo->out
3)  fifo->size < fifo->out  fifo->in     此時 (n-1)fifo->size <  fifo->out < fifo->in < n * fifo->size , 亦即  fifo->in  - fifo->out <= fifo->size
            len = fifo->in % fifo->size - fifo->out %fifo->size = (fifo->in - (n-1)fifo->size) - (fifo->out -(n-1)fifo->size ) = fifo->in - fifo->out 
4)   fifo->in fifo->size < fifo->out  此時fifo->in  已經溢出,由於in out都爲無符號整數,溢出後是又一個無符號整數,即最小0 增大到2**32 ,然後又從最小0 增大到2**32 ,fifo->out <=  2**32fifo->in 和 fifo->out 一定在相鄰的fifo->size區間  有符號整數轉化爲無符號數  y = 2**32 + x    (x  < 0)
           len =  2**32 - fifo->out + fifo->in = fifo->in - fifo->out     
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章