为什么kfifo是环形缓冲区?

stackoverflow 上的这个问题参阅:https://stackoverflow.com/questions/53476760/why-kfifo-is-circular-queue-in-some-blogs/

https://zh.wikipedia.org/wiki/環形緩衝區 中, 提到linux kfifo属于环形缓冲区。且在  Linux Device Drivers chapter 5     page 124 也提到了kfifo 属于环形缓冲区,那么为什么kfifo是环形缓冲区呢?

首先我们看下kfifo的结构体linux-4.16.12\include\linux  :

struct __kfifo {

    unsigned int   in;

    unsigned int   out;

    unsigned int   mask;

    unsigned int   esize;

    void     *data;

}; 

 in :代表入队列的index

 out: 代表出队列的index  

mask: 代表index掩码

esize: 代表kfifo管理的element size

data:  代表数据

其中 mask 非常关键, 首先我们观察kfifo的初始化:

 

其中:

.mask = __is_kfifo_ptr(&(fifo))  ?  0  :  ARRAY_SIZE((fifo).buf) – 1

0 肯定是异常情况, 则 .mask = ARRAY_SIZE((fifo).buf) – 1

在注释中有提到,

 

表示 size 为2的次方, 例如21= 0B10, 22=0B100, 23=0B1000 等, 则对应的mask 为0B1, 0B11, 0B111等。

接下来我们观察在in和out的时候mask的使用:

判断条件先不管,直接来到上图中的435行。可以看到mask的用法:__kfifo->in & __tmp->kfifo.mask,然后将得到的值作为当前值的index.

接下来是get的时候:

在上图中的473行, 用法与put一模一样, 并且我们知道,一个数与1相&数不变。那么我们的in和out这样做有什么意义呢?

其实这就是环形缓冲区的精髓。在初始化中我们可以看到,我们并没有将起始指针指向末尾指针,那么问题就回到了我们的标题,为什么说kfifo属于环形缓冲区?

      请看下面这张图( Linux Device Drivers chapter 5, page124):

 假设我们的fifo size定义的为16,且我们put了10个节点,get了5个节点,则图应该如下所示:

此时in  = 10 & 0B1111 = 10, out = 5 &  0B1111 = 5.

那么在这个基础上, 我们继续put10个节点会发生什么情况?

in = (10 +10)& 0B1111 = 0B0100, 此时 in 在什么位置?

经过mask的”处理”, in 又回到了队列的开头。

      可以看到,仅仅一个mask,就就将一个线性的队列转换成了环形队列。实在是不可思议,却又如此简洁,清晰。

猜你喜欢

转载自www.cnblogs.com/syyxy/p/10033511.html