get和put很类似,首先判断是否有足够的数据取出;在取数据时首先从out取到buffer的末尾,如果不够则从buffer的开始位置取;最后更新out时也是不做模运算,让其溢出。看参看上面put的语句解释,这里就不再多说。
无符号溢出运算kfifo之所以如次的简洁,很大一部分要归功于其in和out的溢出运算。这里就解释下在溢出的情况下,如何保证in - out仍然为缓冲区中的数据长度。首先来看图:
缓冲区为空
put 一堆数据后
get 一堆数据后
put的数据长度超过in到buffer末尾的长度,有一部分从put到buffer的起始位置
以上图片引用自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位无符号整数
执行结果:
第一个输出是将缓冲区填满的状态
第二个输出是将缓冲区取空的状态