KFIFO入门笔记

KFIFO入门笔记

一、 KFIFO的逻辑流程

  1. 申请一块固定空间,作为循环队列。
  2. 入队操作优先copy数据,然后下标in加一。
  3. 出队操作优先取出数据,然后下标out加一。
  4. 为保证内存一致性顺序,使用内存屏障保证下标加一操作在内存copy之后执行。
  5. 下标加一是否要加锁或使用CAS操作,根据具体情况而定,linux下测试uint32_t不用加锁,windows要加锁或使用CAS。
  • 注意以上操作仅适用于单生产者单消费者场景,多生产者,多消费者场景可以分别在两端加锁,使其模拟单生产者单消费者场景。
  • 保证无锁也能保证单消费者线程和单生产者线程操作数据正确的原理是:
  1. 首先保证下标in和out的操作的原子性(使用CAS函数等方式)
  2. 其次是最重要的,保证下标加一操作在内存操作之后执行,这个不仅仅是将内存操作代码写在下标移动代码之前就能实现的,因为程序在执行时,基于操作系统对于性能优化的考虑,会调整命令执行顺序,导致内存操作这种耗时大的命令,即使代码在下标移动命令之前,也会可能在下标移动命令之后执行,导致消费者线程读取下标后,但对应的内存还未拷贝完毕,从而导致数据操作错误,而内存屏障可以解决这个问题。详细的原理介绍,可以搜索缓存一致性

二、无锁队列的几种实现方式

  1. RingBuffer
  2. KFifo
  3. SPSC
  4. Disruptor

三、涉及的技术

  1. CAS指令
  2. 内存屏障

四、需要了解的知识

  1. 缓存一致性顺序

五、KFIFO实现代码

环境:linux , C++11

my_kifo.h

//
// Created by hapYP on 8/26/22.
// Implement a non-lock queue.
//

#ifndef MY_KFIFO_MY_KFIFO_H
#define MY_KFIFO_MY_KFIFO_H


#if 0
#include <stdint.h>
#include <pthread.h>


#define min(x, y)    ((x) < (y) ? (x) : (y))
#define smp_wmb()    __asm__ __volatile__("": : :"memory")

//typedef void* FifoElemType;
struct KFIFO {
    uint32_t in;
    uint32_t out;
    uint32_t mask;   // size - 1;
    uint32_t eSize;   // element size
    void *data;
};

/**
* @brief: Initialize a struct KFIFO.
* @param:
* @retType:
* @retVal:
* @bug:
**/
extern int KFifoInit(struct KFIFO *_kfifo, uint32_t _size, uint32_t _eSize);

/**
* @brief:
* @param:
* @retType:
* @retVal:
* @bug:
**/
extern uint32_t KFifoInLock(struct KFIFO *_kFifo, const void *_buf, uint32_t _len, pthread_spinlock_t *_lock);

/**
* @brief: Copy the data started at _buf and length _len to _kFifo;
* @param:
* @retType: uint32_t
* @retVal: The length copy into kFifo successfully. less than or equal _len;
* @bug:
**/
extern uint32_t KFifoIn(struct KFIFO *_kFifo, const void *_buf, uint32_t _len);

/**
* @brief:
* @param:
* @retType:
* @retVal:
* @bug:
**/
static void KFifoCopyIn(struct KFIFO *_kFifo, const void *src, uint32_t _len, uint32_t _off);

/**
* @brief:
* @param:
* @retType:
* @retVal:
* @bug:
**/
extern uint32_t KFifoOut(struct KFIFO *_kFifo, void *_buf, uint32_t _wantCopyOutLen);

/**
* @brief:
* @param:
* @retType:
* @retVal:
* @bug:
**/
static void KFifoCopyOut(struct KFIFO *_kFifo, void *_buf, uint32_t _copyOutLen, uint32_t _off);


/**
* @brief:
* @param:
* @retType:
* @retVal:
* @bug:
**/
extern uint32_t KFifoUnused(struct KFIFO *_kFifo);

/**
* @brief:
* @param:
* @retType:
* @retVal:
* @bug:
**/
extern int IsFifoEmpty();

/**
* @brief:
* @param:
* @retType:
* @retVal:
* @bug:
**/
extern int IsFifoFull();

/**
* @brief:
* @param:
* @retType:
* @retVal:
* @bug:
**/
extern int KFifoDestroy();

/**
* @brief: Get the minimum of power 2 and greater than _value
* @param:
* @retType:
* @retVal:
* @bug:
**/
extern uint64_t MinUpPowerTwo(uint32_t _value);
#endif

#include <stdint.h>



#define Min(x, y) ((x) > (y) ? (y) : (x))
#define smp_wmb() __asm__ __volatile__("": : :"memory")

/*
 * Define the struct describes the KFifo.
 */
struct KFIFO{
    uint32_t in;  // push in
    uint32_t out;  // pop out
    uint32_t mask;  // total size - 1
    uint32_t elemSize;   // the size of element type
    void *pData;  // the first address of memory for saving the data.
};



/**
* @brief: Initialize struct KFIFO and alloc memory for KFIFO.pData.
* @param: _pKfifo: The start address of struct KFIFO.
* @param: _size: The total bytes of the KFIFO.pData wanting to alloc.
* @param: _elemSize: The bytes of element type.
* @ret: 0: success
* @ret: -1: fail
* @bug: None
**/
int KFifoInit(struct KFIFO *_pKFifo, uint32_t _size, uint32_t _elemSize);

/**
* @brief: Calculate the minimum which is greater than _num and is the power of two.
* @param: _num: number be calculated.
* @ret: Calculate result.
* @bug:
**/
uint64_t RoundupPowOfTwo(uint32_t _num);

/**
* @brief: Return the count of unused block.
* @param: _pKFifo:
* @ret:
*
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以参考以下代码实现一个简单的Linux kfifo demo: ``` #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/slab.h> #include <linux/kfifo.h> #define FIFO_SIZE 1024 MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A Linux kfifo demo"); static char *fifo_buffer; static struct kfifo my_fifo; static int my_open(struct inode *inode, struct file *file) { printk(KERN_INFO "my device opened\n"); return 0; } static int my_close(struct inode *inode, struct file *file) { printk(KERN_INFO "my device closed\n"); return 0; } static ssize_t my_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { ssize_t ret; if (kfifo_is_empty(&my_fifo)) { return -EAGAIN; } ret = kfifo_to_user(&my_fifo, user_buf, count, ppos); if (ret) { printk(KERN_INFO "Read %ld bytes\n", (long)ret); } else { printk(KERN_ERR "Failed to read\n"); } return ret; } static ssize_t my_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { ssize_t ret; if (kfifo_avail(&my_fifo) < count) { return -ENOMEM; } ret = kfifo_from_user(&my_fifo, user_buf, count, ppos); if (ret) { printk(KERN_INFO "Wrote %ld bytes\n", (long)ret); } else { printk(KERN_ERR "Failed to write\n"); } return ret; } static struct file_operations my_fops = { .owner = THIS_MODULE, .open = my_open, .release = my_close, .read = my_read, .write = my_write, }; static int __init my_init(void) { int ret; fifo_buffer = kmalloc(FIFO_SIZE, GFP_KERNEL); if (!fifo_buffer) { return -ENOMEM; } ret = kfifo_init(&my_fifo, fifo_buffer, FIFO_SIZE); if (ret) { kfree(fifo_buffer); return ret; } ret = register_chrdev(0, "my_device", &my_fops); if (ret < 0) { kfifo_free(&my_fifo); kfree(fifo_buffer); return ret; } printk(KERN_INFO "my device registered with major number %d\n", ret); return 0; } static void __exit my_exit(void) { unregister_chrdev(0, "my_device"); kfifo_free(&my_fifo); kfree(fifo_buffer); printk(KERN_INFO "my device unregistered\n"); } module_init(my_init); module_exit(my_exit); ``` 该代码实现了一个简单的字符设备,它使用了Linux内核中提供的kfifo数据结构来实现队列的功能,用户可以通过该设备的文件描述符进行读写操作,读取数据会将队列中的数据写入用户缓冲区,写入数据会将用户缓冲区中的数据写入队列中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值