用环形 FIFO 暂存传感器采样
Linux IIO 的 kfifo 缓冲在采样进入内核与用户态读取之间暂存数据,按先进先出顺序交付采样记录。
工程问题#
采样产生与用户态读取不同步,需要有界缓冲吸收短时速率差,同时避免为每个样本单独分配链表节点。
实际实现#
IIO 按采样记录大小配置 kfifo。底层用数组及 in、out 计数器组织环形存储,容量取二次幂,索引通过掩码回绕;跨越末尾的数据分两段复制。
处理流程#
- 按记录大小和缓冲长度分配数组。
- 采样路径用 kfifo_in 追加一个记录。
- 到数组末端时继续从头部写入。
- 读取路径按 FIFO 顺序复制给用户态并推进读位置。
源码片段#
固定版本 · 原文摘录93 unsigned int size = fifo->mask + 1;94 unsigned int esize = fifo->esize;95 unsigned int l;9697 off &= fifo->mask;98 if (esize != 1) {99 off *= esize;100 size *= esize;101 len *= esize;102 }103 l = min(len, size - off);104105 memcpy(fifo->data + off, src, l);106 memcpy(fifo->data, src + l, len - l);107 /*108 * make sure that the data in the fifo is up to date before109 * incrementing the fifo->in index counter110 */111 smp_wmb();112}off 与 mask 相与得到环内位置;先复制到数组末尾,再把剩余数据写到头部。esize 把元素单位换为字节,smp_wmb 保证数据写入先于调用方发布新的 in 计数;这不是任意多生产者的无锁协议。
片段为 kfifo_copy_in 的连续函数体。可写容量由调用者 __kfifo_in 检查,写计数器在复制完成后推进;IIO 的一元素入队不能误读成一字节。
收益与取舍#
工程分析带来的收益
- 循环复用预分配存储,避免逐样本节点分配。
- 有界容量让缓冲内存开销明确。
付出的代价
- 缓冲满时本次入队失败,IIO 回调返回 EBUSY。
- 多生产者或多消费者场景仍需相应同步。
可比较的选择
链式队列可以逐节点增长,但需要额外分配、指针存储和容量控制。
版本与适用范围#
对应 IIO 使用 kfifo 的采样缓冲路径;一条记录可包含多个字节。环形表示不自动保证任意多线程组合均无需锁。
参考来源#
源码、官方文档与原始论文drivers/iio/buffer/kfifo_buf.c · iio_store_to_kfifo; iio_read_kfifo; __iio_allocate_kfifo
相关实现与比较#
4 个相关案例nginx 解析 upstream 块时,用 ngx_array_t 连续保存服务器条目;每遇到一条 server 指令便追加一个元素。
Linux BPF 验证器的 check_cfg 使用显式指令栈执行非递归 DFS,检查跳转目标、可达性及控制流结构。
QEMU 线程池用尾队列组织待处理请求:提交时尾插,worker 持锁从头部领取,然后在锁外执行工作。
Git 把内存中的 index 条目指针保存在有序、可增长的数组中,按路径和合并 stage 二分查找位置。