标签:特点 token 取模运算 ref turn lin 输入输出 多个 设置
Linux kernel里面从来就不缺少简洁,优雅和高效的代码
比如,通过限定写入的数据不能溢出和内存屏障实现在单线程写单线程读的情况下不使用锁。因为锁是使用在共享资源可能存在冲突的情况下。还用设置buffer缓冲区的大小为2的幂次方,以简化求模运算,这样求模运算就演变为 (fifo->in & (fifo->size - 1))。通过使用unsigned int为kfifo的下标,可以不用考虑每次下标超过size时对下表进行取模运算赋值,这里使用到了无符号整数的溢出回零的特性。由于指示读写指针的下标一直在增加,没有进行取模运算,知道其溢出,在这种情况下写满和读完就是不一样的标志,写满是两者指针之差为fifo->size,读完的标志是两者指针相等。后面有一篇博客还介绍了VxWorks下的环形缓冲区的实现机制点击打开链接,从而可以看出linux下的fifo的灵巧性和高效性。
kfifo
主要有以下特点:
in-out
的结果为缓冲区中已存放的数据长度,这也是最能体现kfifo
实现技巧的地方;kfifo
的无锁并发访问,多个消费者、生产者的并发访问还是需要加锁的。本文主要以下三个部分:
kfifo
的实现及简要分析kfifo
实现的循环缓冲区,并进行一些测试关于内存屏障的本文不作过多分析,可以参考WikiMemory Barrier。另外,本文所涉及的整数都默认为无符号整数,不再做一一说明。
kfifo
要保证其缓存空间的大小为2的次幂,如果不是则向上取整为2的次幂。其对于2的次幂的判断方式也是很巧妙的。如果一个整数n是2的次幂,则二进制模式必然是1000...
,而n-1的二进制模式则是0111...
,也就是说n和n-1的每个二进制位都不相同,例如:8(1000)和7(0111);n不是2的次幂,则n和n-1的二进制必然有相同的位都为1的情况,例如:7(0111)和6(0110)。这样就可以根据 n & (n-1)
的结果来判断整数n是不是2的次幂,实现如下:/*
判断n是否是2的幂
若n为2的次幂, 则 n & (n-1) == 0,也就是n和n-1的各个位都不相同。例如 8(1000)和7(0111)
若n不是2的次幂, 则 n & (n-1) != 0,也就是n和n-1的各个位肯定有相同的,例如7(0111)和6(0110)
*/
static inline bool is_power_of_2(uint32_t n)
{
return (n != 0 && ((n & (n - 1)) == 0));
}
100...
,故如果正数k不是n的次幂,只需找到其最高的有效位1所在的位置(从1开始计数)pos,然后1 << pos
即可将k向上取整为2的次幂。实现如下:static inline uint32_t roundup_power_of_2(uint32_t a)
{
if (a == 0)
return 0;
uint32_t position = 0;
for (int i = a; i != 0; i >>= 1)
position++;
return static_cast<uint32_t>(1 << position);
}
fifo->in & (fifo->size - 1) 再比如这种写法取模,获取已用的大小。这样用逻辑与的方式相较于加减法更有效率
讲解最详细的一篇https://blog.csdn.net/linyt/article/details/53355355
另外一个https://www.cnblogs.com/wangguchangqing/p/6070286.html
标签:特点 token 取模运算 ref turn lin 输入输出 多个 设置
原文地址:https://www.cnblogs.com/wangshaowei/p/11559522.html