无锁环形缓冲区队列 kfifo

67 篇文章 1 订阅

kfifo的移植

两个月前,我花了两天时间,查找Linux内核里kfifo的相关资料,将其从内核层移植到应用层,并成功应用于多线程CAN总线采集程序(一个线程接收/一个线程输出)。kfifo.c是从Linux 5.3 stable内核代码里复制出来的,路径是lib/kfifo,对应的kfifo.h路径是include/linux/kfifo.h。由于kfifo是内核里的代码,应用层无法直接使用,我做了如下修改:

  • 注释掉无关的或不必要的代码,如对内核头文件的引用,如涉及dma、sgl的代码

  • 重新实现某些功能,如采用SO上的代码取代了roundup_pow_of_two,用GCC内置函数__sync_synchronize取代了smp_wmb,重新定义了ARRAY_SIZE

代码仓库:https://github.com/liigo/kfifo

kfifo的使用

很简单就三点:用 DEFINE_KFIFO 定义变量并初始化,用 kfifo_in 向缓冲区内写入数据,用 kfifo_out 从缓冲区取出数据。DEFINE_KFIFO宏参数1是一个变量名(调用者只给一个名称,宏内部负责定义类型和变量),参数2是成员类型(自定义结构体,也可以是任意其他类型),参数3是缓冲区成员个数(必须是2的幂)。

DEFINE_KFIFO(g_canbuf, buf_item_t, 1024);
kfifo_in(&g_canbuf, &bufitem, 1);
int n = kfifo_out(&g_canbuf, &bufitem, 1);

kfifo代码里大量使用宏,理解起来很费劲,主要因为宏参数的类型不明确。


20210528 Liigo 补记,关于kfifo对象的定义和初始化,大致有以下几种模式:

  • 模式1. DEFINE_KFIFO(name, int, 1024); 定义变量name并对其初始化。可用于定义全局变量和结构体变量。
  • 模式2. DECLARE_KFIFO(name, int, 1024); + INIT_KFIFO(name); 定义和初始化分开,这样一来前者可以出现在结构体里面用于定义结构体成员,比模式1更灵活。另外某些旧版gcc不支持模式1的也可使用此模式。
  • 模式3. DECLARE_KFIFO_PTR(name, int); + kfifo_init(&name, buf, size); 另一种形式的定义+初始化,缓冲区不在对象内部而是由程序员另行动态分配。相比前两模式,此模式可在运行时确定缓冲区大小(单位是字节),更加灵活;而且如此定义的name变量占用内存较小(32位系统下占24字节)。

kfifo的设计和实现

关于Linux内核kfifo的设计和实现的精妙之处,推荐大家阅读如下文章:

需要特别说明的是,以上第三方分析文章所基于的kfifo内核版本都相对陈旧,而本文所采用的代码所属内核版本是当前最新的5.3。

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值