1) ring buffer位首尾相接的buffer,即相似生活中的圓形跑道;linux
2) 空閒空間+數據空間=ring buffer大小編程
3) ring buffer的讀寫,相似生活中在圓形跑道上的追趕遊戲,領跑者位write,追趕着爲read函數
4) 若是read跑的太快,追上write,追趕者read要停下來,不然遊戲結束。即保證沒有數據空間時,再也不從ring buffer中讀取數據;spa
5) 若是write跑的太快,反過來套圈要超過read,此時領跑者write也要停下來。即保證沒有空閒空間時,再也不往ring buffer中寫入數據;.net
6) 因此,read和write之間的距離老是介於開區間(0, buffer大小)設計
假設buffer的大小爲size,讀指針爲in,寫指針爲outunix
1) 在計算機中,整型數據加到最大值後溢出會回捲到0,從頭開始)指針
2)buffer的長度必須是2的n次冪rest
3) buffer空閒空間和數據空間的大小orm
1> 空閒空間的大小=size-in+out
2> 空閒空間的大小=in-out
本設計總能保證in前out前面的,in跑出unsigned int邊界溢出後回捲。
由於buffer的大小是2的n次冪,而unsigned int也是2的n次冪(32位機器上,n=32),通常buffer大小不會超過unsigned int大小,即unsigned int被分紅m個整塊(m>=1)
空閒空間=size-數據控件=size-(in-out)=size-in+out
第2種狀況:(in跑到unsigned int的邊界後,溢出了)
out+數據空間=in,這個等式仍然成立。
因此:空閒空間=size-in+out,亦成立
設落在ring buffer內寫指針爲__in,讀指針爲__out,須要寫入的空間大小爲len, 其中
1. __in = fifo->in % (fifo->size - 1) (讀寫指針都是從0開始算起)
2. __out = fifo->out % (fifo->size - 1)
3. __size = fifo->size
4. len <= 空閒空間大小
這種狀況下,須要寫兩塊buffer,作兩次拷貝動做,設須要寫入的大小爲len,第一塊空閒空間大小爲left1,第二塊爲left2,須要第一次拷貝的大小爲len1,第二次拷貝的大小爲len2,len1 + len2 = len:
1. left1 = _size-__in;
2. len1 = min(len, left1) = min(len, _size-__in);
3. left2 = __out;
4. len2 = len - len1
這種狀況下,須要寫一塊buffer,作一次拷貝動做:
1. left1 = __out - __in <= __size - __in;
2. 而寫入長度len <= 空閒空間大小,因此len <= left1 <= __size - __in,因此len1 = len, len1 = min(len, __size - __in)仍然成立
3. left2 = 0;
4. len2 = 0 = len -len1
總結以上兩種情形,第一塊空閒空間大小爲left1,第二塊爲left2,須要第一次拷貝的大小爲len1,第二次拷貝的大小爲len2,len1 + len2 = len,則通用狀況以下:
1. len <= 空閒空間大小
2. len1 = min(len, _size-__in);
3. len2 = len -len1
unsigned int __kfifo_put(struct kfifo *fifo,
unsigned char *buffer, unsigned int len)
{
unsigned int l;
len = min(len, fifo->size - fifo->in + fifo->out);
/* 前提條件寫入大小len不超過空閒空間大小 */
smp_mb();
/* 第一塊寫入空閒空間,大小爲min(len, size-in) */
l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
/* 第二塊寫入空閒空間,大小爲len-min(len, size-in) */
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;
return 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;
}
buffer大小是2的K次方:size = (size & (size - 1)
將SIZE 調整到最近的2^k:
思想很簡單,就是找出當前數的二級制中最大位爲1位的位置,而後用1左移位數便可。
好比數據5,它的二進制形式爲101,最高位爲1的位置是3,而後左移3位,等於1000,即數字8。也就是數字8是5的接近的2的整數次冪。
思想很簡單,最主要的任務就是找到最高位爲1的位置。這個有專門的AT&T彙編指令bsrl。這個指令是個32位指令,位置範圍是0到31。
若是bsrl %eax 5的範圍結果是2,因此咱們在用它的時候加1便可。固然0是須要考慮的,咱們把它的值賦值成-1,這樣函數結果的範圍就編程1到32。具體實現以下:
static inline int fls(int x)
{
int r;
__asm__("bsrl %1,%0\n\t"
"jnz 1f\n\t"
"movl $-1,%0\n"
"1:" : "=r" (r) : "rm" (x));
return r+1;
}
固然,若是不用匯編也是能夠實現的,咱們用C語言來實現它。就是不斷左移,直到數值爲0,記下它左移的次數,既是它最高位爲1的位置。
static inline int fls(int x)
{
int position;
int i;
if(0 != x)
{
for (i = (x >> 1), position = 0; i != 0; ++position)
i >>= 1;
}
else
{
position = -1;
}
return position+1;
}
最後把要獲得的數字用1左移那麼屢次數,便可。考慮到0的特殊性,咱們把數字都減1,其餘都不會受影響,這樣0會取值成-1,而後取到的位置爲32,1左移32位後仍是爲0。
實現代碼以下:
static inline unsigned int roundup_pow_of_two(unsigned int x)
{
return 1UL << fls(x - 1);
}
版權