现在的位置: 首页 > 综合 > 正文

Linux中kfifo数据结构的精妙之处:即使溢出仍然正确

2017年12月12日 ⁄ 综合 ⁄ 共 927字 ⁄ 字号 评论关闭

kfifo是linux内核中的环形缓冲区,实现了先进先出的队列数据结构。以下为kfifo的数据结构定义(内核版本2.6.33.20):

struct kfifo {
        unsigned char *buffer;  // 环形缓冲区的大小
        unsigned int size;      // 环形缓冲区的大小,必须是2的冥
        unsigned int in;        /* data is added at offset (in % size) */
        unsigned int out;       /* data is extracted from off. (out % size) */
};

这个结构有以下特性:

1. 初始状态下,in == out == 0。

2. 入队导致in递增,出队导致out递增。注意in和out总是递增的,从来不会减少。

3. 计算in对应的缓冲区索引的公式是fifo.in& (fifo.size
- 1),见__kfifo_off函数

4. 计算缓冲区中元素数量的公式是fifo.in - fifo.out,见kfifo_size函数。

根据以上特性,在运行的时候in和out迟早会溢出。然而即使溢出,kfifo_size函数仍然是对的,我们来分析一下。

预备知识:在32位机器上,假设N为正整数,static_cast<unsigned int>(-N) == 2**32 – N。也就是说,-N的补码的比特位,如果强制解释成正整数,其数值为2**32-N。

分情况证明:

1.      如果in和out都没有溢出,或者in和out都溢出了,kfifo_size是对的。

2.      如果in溢出了,out没有溢出。记in在溢出之前的数值为in_nofl,则in_nofl == 2**32+in。由于fifo.in < fifo.out,fifo.in – fifo.out为负数,因此static_cast<unsigned int>(fifo.in-fifo.out) == 2**32 -(fifo.out-fifo.in) == (2**32+fifo.in)-fifo.out == in_nofl – fifo.out。因此kfifo_size是正确的。

3.      不可能in没有溢出同时out溢出。

 

kfifo的这一点真是精妙,要是我来搞,肯定需要一大段的分支判断才能保证程序的正确性。

抱歉!评论已关闭.