linux内核kfifo

来源:互联网 发布:淘宝的在线客服在哪里 编辑:程序博客网 时间:2024/06/05 20:05
===============================   博客点滴积累,部分话语和知识点来源于网络,感谢网络资源的提供者======
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     
0 0
原创粉丝点击