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**32,fifo->in 和 fifo->out 一定在相鄰的fifo->size區間
有符號整數轉化爲無符號數 y = 2**32 + x (x < 0)
len = 2**32 - fifo->out + fifo->in = fifo->in - fifo->out
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.