前面我们详细说明了下内核队列 Kfifo 的源码。详情请跳转内核循环队列

最后的 自我评价 现在看来,令人汗颜,大牛们给出的代码精华,我等小人猪油蒙了心,却认为是不和适宜的瑕疵。所以,我对 Kfifo 位置更新做了个整理。

问题提出

我们再来看看源码:

  1. unsigned int __kfifo_put(struct kfifo *fifo,
  2. const unsigned char *buffer, unsigned int len)
  3. {
  4. unsigned int l;
  5. //buffer中空的长度
  6. len = min(len, fifo->size - fifo->in + fifo->out);
  7. /*
  8. * Ensure that we sample the fifo->out index -before- we
  9. * start putting bytes into the kfifo.
  10. */
  11. smp_mb();
  12. /* first put the data starting from fifo->in to buffer end */
  13. l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
  14. memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
  15. /* then put the rest (if any) at the beginning of the buffer */
  16. memcpy(fifo->buffer, buffer + l, len - l);
  17. /*
  18. * Ensure that we add the bytes to the kfifo -before-
  19. * we update the fifo->in index.
  20. */
  21. smp_wmb();
  22. fifo->in += len; //每次累加,到达最大值后溢出,自动转为0
  23. return len;
  24. }

我们最大的疑问来自于 23 行。有两个灵魂拷问:

  • 我们更新了 fifo->in ,却不是和 fifo 的大小进行求余处理。而是进行累加操作。
  • 我们是不是需要把队列的大小设置成无符号整型溢出值一样大?
  • 每次累加到最大值溢出,固然是很不错的想法,但是,如果我的缓冲区大小为 16M ,难道也要到 unsigned int 最大值溢出时,才返回0吗?
  • 我们这样累加, fifo->in 值会不断增大,但是我们设置的size比较小。要写入的数据咋办?

分析问题

我们提出了几个问题,这些个问题的关键点,是在于无符号整型的溢出值很大,我们设置的队列大小比较小,而写入读取数据的位置在不断累计。这二者之间出现了矛盾。所以,我们先来看看对于队列的操作。

  1. memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
  2. /* then put the rest (if any) at the beginning of the buffer */
  3. memcpy(fifo->buffer, buffer + l, len - l);

在对 buffer 实际操作时,我们使用到了两个变量, len 、l 。我们再看看他们的含义。

  1. len = min(len, fifo->size - fifo->in + fifo->out);
  2. l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));

从代码中,我们可以看到:

  • len的含义是实际插入数据的长度。数据大于剩余空间,则实际插入剩余空间大小,如果数据小于剩余空间大小,则实际插入剩余空间大小。
  • l的意义参考图二tail free 和 实际插入数据的最小值。

我们发现,这里 lenl 数值来源,没有涉及到具体的位置使用。都是和相对于 fifo->in 、fifo->out。所有求相对值时,也都用到了 fifo->size .所以,这里有一个关键条件,那就是:

:::success fifo->in - fifo->out <= fifo->size

:::

只要满足了上述关系,不管你 inout 怎么累计赋值,都是符合要求的。

在加上对无符号整型循环归零的特性,就完美解决了循环操作的数据处理。

总结

上面基本说明了 fido->infifo->out 可以累计,不对 fifo->size 做求余处理。其实基核心就是一点, inout 之间的差值一定是小于等于队列大小的。无符号整型溢出归零的特性解决了循环问题,但是给理解整个问题带来了一定难度。