ring buffer

1. 有關ring buffer的理解

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大小)設計

2. linux2.6內核,kfifo的理解

假設buffer的大小爲size,讀指針爲in,寫指針爲outunix

1) 在計算機中,整型數據加到最大值後溢出會回捲到0,從頭開始)指針

2)buffer的長度必須是2的n次冪rest

3) buffer空閒空間和數據空間的大小orm

1> 空閒空間的大小=size-in+out

2> 空閒空間的大小=in-out

2.2 對數據空間大小計算的理解

本設計總能保證in前out前面的,in跑出unsigned int邊界溢出後回捲。

由於buffer的大小是2的n次冪,而unsigned int也是2的n次冪(32位機器上,n=32),通常buffer大小不會超過unsigned int大小,即unsigned int被分紅m個整塊(m>=1)

第1種狀況:

out+數據空間=in

空閒空間=size-數據控件=size-(in-out)=size-in+out

第2種狀況:(in跑到unsigned int的邊界後,溢出了)

out+數據空間=in,這個等式仍然成立。

因此:空閒空間=size-in+out,亦成立

2.3 寫操做分析(讀操做相似,再也不贅述)


2.3.1 基本狀況

設落在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 <= 空閒空間大小

2.3.2 寫指針沒有回捲

這種狀況下,須要寫兩塊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

2.3.3 寫指針回捲

這種狀況下,須要寫一塊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

2.3.4 兩種特殊狀況通常化

總結以上兩種情形,第一塊空閒空間大小爲left1,第二塊爲left2,須要第一次拷貝的大小爲len1,第二次拷貝的大小爲len2,len1 + len2 = len,則通用狀況以下:

1. len <= 空閒空間大小

2. len1 = min(len, _size-__in);

3. len2 = len -len1



  1. unsigned int __kfifo_put(struct kfifo *fifo, 

  2.              unsigned char *buffer, unsigned int len) 

  3. { 

  4.     unsigned int l; 


  5.     len = min(len, fifo->size - fifo->in + fifo->out); 


  6.     /* 前提條件寫入大小len不超過空閒空間大小 */ 


  7.     smp_mb(); 


  8.     /* 第一塊寫入空閒空間,大小爲min(len, size-in) */ 

  9.     l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); 

  10.     memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l); 


  11.     /* 第二塊寫入空閒空間,大小爲len-min(len, size-in) */ 

  12.     memcpy(fifo->buffer, buffer + l, len - l); 


  13.     /* 

  14.      * Ensure that we add the bytes to the kfifo -before- 

  15.      * we update the fifo->in index. 

  16.      */ 


  17.     smp_wmb(); 


  18.     fifo->in += len; 


  19.     return len; 

  20. }


  21. unsigned int __kfifo_get(struct kfifo *fifo, 

  22.              unsigned char *buffer, unsigned int len) 

  23. { 

  24.     unsigned int l; 


  25.     len = min(len, fifo->in - fifo->out); 


  26.     /* 

  27.      * Ensure that we sample the fifo->in index -before- we 

  28.      * start removing bytes from the kfifo. 

  29.      */ 


  30.     smp_rmb(); 


  31.     /* first get the data from fifo->out until the end of the buffer */ 

  32.     l = min(len, fifo->size - (fifo->out & (fifo->size - 1))); 

  33.     memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l); 


  34.     /* then get the rest (if any) from the beginning of the buffer */ 

  35.     memcpy(buffer + l, fifo->buffer, len - l); 


  36.     /* 

  37.      * Ensure that we remove the bytes from the kfifo -before- 

  38.      * we update the fifo->out index. 

  39.      */ 


  40.     smp_mb(); 


  41.     fifo->out += len; 


  42.     return len; 

  43. }


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);
}

版權

相關文章
相關標籤/搜索