模仿Linux内核kfifo实现的循环缓存(3)

get和put很类似,首先判断是否有足够的数据取出;在取数据时首先从out取到buffer的末尾,如果不够则从buffer的开始位置取;最后更新out时也是不做模运算,让其溢出。看参看上面put的语句解释,这里就不再多说。

无符号溢出运算

kfifo之所以如次的简洁,很大一部分要归功于其in和out的溢出运算。这里就解释下在溢出的情况下,如何保证in - out仍然为缓冲区中的数据长度。首先来看图:

缓冲区为空

模仿Linux内核kfifo实现的循环缓存

put 一堆数据后

模仿Linux内核kfifo实现的循环缓存

get 一堆数据后

模仿Linux内核kfifo实现的循环缓存

put的数据长度超过in到buffer末尾的长度,有一部分从put到buffer的起始位置

模仿Linux内核kfifo实现的循环缓存

以上图片引用自Linux内核数据结构之kfifo,其对kfifo的分析也很详细。

前三种情况下从图中可以很清晰的看出in - out为缓冲区中的已有的数据长度,但是最后一种发现in跑到了out的前面,这时候in - out不是应该为负的么,怎么能是数据长度?这正是kfifo的高明之处,in和out都是无符号整数,那么在in < out 时in - out就是负数,把这个负数当作无符号来看时,其值仍然是缓冲区中的数据长度。这和in累加到溢出的情况基本一致,这里放在一起说。

这里使用8位无符号整数来保存in和out,方便溢出。这里假设out = 100,in = 255,size = 256,如下图

/* -------------------------------------- | | | | -------------------------------------- out = 100 in = 250 这时缓冲区中已有的数据为:in - out = 150,空闲空间为:size - (in - out) = 106 向缓冲区中put10个数据后 -------------------------------------- | | | | -------------------------------------- in out 这时候 in + 10 = 260 溢出变为in = 4;这是 in - out = 4 - 100 = -96,仍然溢出-96十六进制为`0xA0`,将其直接转换为有符号数`0xA0 = 160`,在没put之前的数据为150,put10个后,缓冲区中的数据刚好为160,刚好为溢出计算结果。 */

进行上述运算的前提是,size必须为2的次幂。假如size = 257,则上述的运行就不会成功。

测试实例

上面描述都是基于运算推导的,下面据结合本文中的代码进行下验证。
测试代码如下:设置空间大小为128,in和out为8位无符号整数

int main() { uint8_t output[512] = { 0 }; uint8_t data[256] = { 0 }; for (int i = 0; i < 256; i++) data[i] = i; kfifo fifo(128); fifo.put(data, 100); fifo.get(output, 50); fifo.put(data, 30); auto c = fifo.put(data + 10, 92); cout << "Empty:" << fifo.isEmpty() << endl; cout << "Left Space:" << fifo.left() << endl; cout << "Length:" << fifo.length() << endl; uint8_t a = fifo.size - fifo.in + fifo.out; uint8_t b = fifo.in - fifo.out; cout << "=======================================" << endl; fifo.get(output, 128); cout << "Empty:" << fifo.isEmpty() << endl; cout << "Left Space:" << fifo.left() << endl; cout << "Length:" << fifo.length() << endl; cout << "======================================" << endl; fifo.put(output, 100); cout << "Empty:" << fifo.isEmpty() << endl; auto d = static_cast<uint8_t>(fifo.left()); auto e = static_cast<uint8_t>(fifo.length()); printf("Left Space:%d\n", d); printf("Length:%d\n", e); getchar(); return 0; }

执行结果:

模仿Linux内核kfifo实现的循环缓存

第一个输出是将缓冲区填满的状态

第二个输出是将缓冲区取空的状态

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/252d08d9b0f73fec94c77a72d82a2133.html