kfifo
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 可见,所以需要采取一些辅助措施来保证这个效果。