环形队列的实现

0. 目录

1. 起因

做嵌入式的过程中,很容易用到环形队列来做一个buffer缓存的管理。个人感觉这么几种场景用的比较多。

  • 多线程场景
    比如A模块给B模块发数据,B模块通过中断实时收取,但是不能实时处理,就需要进行缓存,在另外一个线程中处理。
  • 输入速率不稳定,但是要求输出稳定的场景。
    比如我们算法计算结果不准时,但是客户要求can输出是准确的周期,就要先放到环形队列里,然后再进行发送处理。

优势在于:多线程中操作也可以无锁操作(单入单出)

2. 概念

队列 (Queue):是一种先进先出(First In First Out ,简称 FIFO)的线性表,只允许在一端插入(入队),在另一端进行删除(出队)。
一般队列分为:

  • 普通队列
  • 环形队列

用的数据结构也有两种

  • 线性数组
  • 链表

3. 实现

3.1 结构体

一般环形队列结构体都如下:

typedef struct RingQueue
{
	int nRdIdx;
	int nWrIdx;
	(int nLength;)(可选:数组长度)
	(int nFull;) (可选:是否满)
	BufMem tbuf[BufLen];
}

其中,RdIdx,WrIdx这种读写标志 也会用Head、Tail 这种“头尾”概念来进行描述。

3.2 存取机制

我司内部存取数据一直有这样的称呼:

  • D-buf
  • Q-buf

具体来源哪里我还没搞清楚,好像是一个视频流的架构里的称呼。

或者

  • enqueue
  • dequeue

3.2.1 存

存的时候,将数据放入,修改WrIdx参数。
有空闲buf的时候很好处理,当没有空闲的时候,有两种处理:

  • 报错,不存新的值进去
  • 记录,覆盖存储
    存数据的步骤:
  1. 判断当前队列是否满((nWrIdx+1)%BufLen = nRdIdx),是否可以写入数据
  2. 将新的数据放入buf,同时nWrIdx加1。

3.2.2 取

当buf不空的时候((nWrIdx+1)%BufLen != nRdIdx),可以从内存中取走nReadIdx所指的buf,然后改写nReadIdx指向下一块buf。

4. Kfifo

Kfifo是linux内核中用的比较广泛的一种无锁环形队列。
简介高效。
其实多看看别人实现的经典代码,往往能收获很多。
相关资料很多很多,有很多详细解析。
这也是最提升的地方。

struct kfifo {
    unsigned char *buffer;    /* the buffer holding the data */
    unsigned int size;    /* the size of the allocated buffer */
    unsigned int in;    /* data is added at offset (in % size) */
    unsigned int out;    /* data is extracted from off. (out % size) */
    spinlock_t *lock;    /* protects concurrent modifications */
};

kfifo有几个特别的地方,很有技巧:

  1. buffer长度都用2的N次幂,这样的话,每次取余计算就不用%符号,而是可以做位运算&,即:
  • 当x为2的幂的时候,取模运算%x都可以用与运算&(x-1)代替
  • 读写的index,in和out,每次如果读取或者写入一定的长度,直接加就行,不用取模运算,即 in = in + len; out = out + len; 加超过unsigned int范围之后会自动环回。
    数据空间(已写入空间)长度为:kfifo->in - kfifo->out
    剩余空间(可写入空间)长度为:kfifo->size - (kfifo->in - kfifo->out)
  • 多次利用min宏来运算,减少了条件分支。
  • 内存屏障(这块我也不太懂)

不过说实话,在内核里这么写,确实效率很高又巧妙,但是在上层我还是愿意老老实实的写,因为现在感觉自己记忆力越来越差了,我担心有一天我看不懂自己写的代码。。还是可读性最重要。蓝瘦。

5. 参考链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值