先上内核循环缓冲结构体的定义:
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 */
};
如果对“Linux内核中的循环缓冲区”不是很了解的话,可以先参考 这里。内核中有关kfifo.c和kfifo.h两个文件的源码以及该问题的具体情况,可以查看这里。
对于结构体内的in和out两个变量,内核是作如下处理的:1、在读入数据时增加in;2、在取出数据时增加out;3、当检测到两个相等的时候将它们复位归0。1和2不作讨论和分析,针对第3点的处理,内核代码如下:
static inline unsigned int kfifo_get(struct kfifo *fifo,
unsigned char *buffer, unsigned int len)
{
unsigned long flags;
unsigned int ret;
spin_lock_irqsave(fifo->lock, flags);
ret = __kfifo_get(fifo, buffer, len);
/*
* optimization: if the FIFO is empty, set the indices to 0
* so we don't wrap the next time
*/
if (fifo->in == fifo->out)
fifo->in = fifo->out = 0;
spin_unlock_irqrestore(fifo->lock, flags);
return ret;
}
问题:当数据写入速度大于读取速度的时候,in和out的值将永远不会相等,也就是说buffer永远是有数据的,这样的话in和out都存在超出自身数值表示范围,从而导致错误?
针对这个问题,不知大家有什么好的建议?
之前看錯你的問題了。
從原始碼的實作上來說,in和out的確是有可能會出現溢出,但是出現的情況非常極端:每次讀取資料的時候都比目前緩衝區中的資料還少、而且這種情況持續直到寫入的資料超過4GB。通常應該是不會遇到的;鑑於墨菲定律可能帶來的惡果,的確還是得考慮一下。
不過可以再想想,溢出了就真的會導致程式出錯嗎?
回頭再仔細看看 __kfifo_put() 裡面的程式碼,在寫入的時候是這樣實現的:
注意(fifo->in & (fifo->size - 1) 這裡用了& 符號,而不是直接% fifo->size ,也就是說,初始化的時候size必然得設定成2的n次方(這個限制在核心裡很合理,因為核心分配的空間通常是2的倍數,例如一個page)。
在像x86這種溢位跟取模運算等價的處理器上,對於目前的寫入操作其實「剛好」沒有風險。同樣的,由於in/out都是 unsigned int ,在後續的 kfifo_get/kfifo_len 裡面 in - out (比如說 2 - 4294967295,你可以試試),結果仍然「正好」是正確的。你既然都翻到源碼了,為什麼不仔細看看
的實作呢?kfifo_put()
它所呼叫的
的程式碼足以解釋了你的問題:__kfifo_put()
在寫入之前會先比較待寫入資料的長度和緩衝區的最大可寫入長度,取其小者,寫入,然後返回寫入的資料長度。具體程式碼沒幾行,自己去看吧。__kfifo_put()