kfifo 的一些伪代码

 1 kfifo_len()
 2         out = LOAD fifo->out
 3         smp_rmb()
 4         len = LOAD fifo->in - out
 5 
 6 
 7 kfifo_in()                                      kfifo_out()
 8         kfifo_len()                                     kfifo_len()
 9 
10         smp_mb()                                        smp_rmb()
11 
12         off = LOAD fifo->in + off                       off = LOAD fifo->out + off
13 
14         /* memcpy */                                    /* memcpy */
15 
16         smp_wmb()                                       smp_mb()
17 
18         STORE fifo->in += off                           STORE fifo->out += off

kfifo_in 只修改 fifo->in 的值,含一个 STORE 指令,及若干 fifo->out fifo->in 的 LOAD 指令
kfifo_out 相反,只修改 kfifo->out 的值,同样含一个 STORE 指令及若干 LOAD 指令

把代码中的内存屏障去掉

kfifo_len()
        out = LOAD fifo->out
        /* smp_rmb() */
        len = LOAD fifo->in - out

kfifo_in()                                      kfifo_out()

        kfifo_len()                                     kfifo_len()

        /* smp_mb() */                                  /* smp_rmb() */

        off = LOAD fifo->in + off                       off = LOAD fifo->out + off

        /* memcpy */                                    /* memcpy */

        /* smp_wmb() */                                 /* smp_mb() */

        STORE fifo->in += off                           STORE fifo->out += off



两个函数的内存访问的会产生干扰的操作位于 kfifo_len() 中,假设现在是单生产者单消费者模式,考虑下面的情况

        kfifo_in()
t1              out = LOAD fifo->out
t2              /* smp_rmb() */
t3              len = LOAD fifo->in - out
t4              /* smp_mb() */                          kfifo_out()
t5              off = LOAD fifo->in + off                       out = LOAD fifo->out
t6              /* memcpy */                                    /* smp_rmb() */
t7              /* smp_wmb() */                                 len = LOAD fifo->in - out
t8              STORE fifo->in += off                           /* smp_rmb() */
t9                                                              off = LOAD fifo->out + off
t10                                                             /* memcpy */
t11                                                             /* smp_mb() */
t12                                                             STORE fifo->out += off


        kfifo_out()
t1              out = LOAD fifo->out
t2              /* smp_rmb() */
t3              len = LOAD fifo->in - out
t4              /* smp_rmb() */                         kfifo_in()
t5              off = LOAD fifo->out + off                      out = LOAD fifo->out
t6              /* memcpy */                                    /* smp_rmb() */
t7              /* smp_mb() */                                  len = LOAD fifo->in - out
t8              STORE fifo->out += off                          /* smp_mb() */
t9                                                              off = LOAD fifo->in + off
t10                                                             /* memcpy */
t11                                                             /* smp_wmb() */
t12                                                             STORE fifo->in += off

后执行的函数在 t8 时刻,实际上内存已经写入或者读出,但是 in 和 out 并没有发生改变就被另外一个函数读取。
而这并不会产生错误,但是写入或者读出的数据都比实时的数据偏小,这也是 kfifo 的高明之处。
如果出队入队的操作特别频繁的话,对结果没影响。

若是要保持实时的顺序,即 t6 -> t8,一个函数的 memcpy 操作立刻对另一个 memcpy 可见,所以需要采取一些辅助措施来保证这个效果。