您的位置:首页 > 运维架构 > Linux

学习小记--linux内核kfifo

2015-12-26 16:13 411 查看
=============================== 博客点滴积累,部分话语和知识点来源于网络,感谢网络资源的提供者======

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**32,fifo->in
和 fifo->out 一定在相邻的fifo->size区间 有符号整数转化为无符号数 y = 2**32 + x (x < 0)
len = 2**32 - fifo->out + fifo->in = fifo->in - fifo->out
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: