kfifo简单使用

本文介绍了一个简单的KFIFO实现,该实现从Linux内核源码中提取并修改而来,适用于应用层。文章详细展示了如何初始化、分配、释放KFIFO,以及如何往KFIFO中写入和读取数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

kfifo简单应用.  从linux内核源码中获取并修改得到.并在应用层使用之. 备忘

kfifo.c

/*
 * A simple kernel FIFO implementation.
 *
 * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

//#include <linux/kernel.h>
//#include <linux/module.h>
//#include <linux/slab.h>
//#include <linux/err.h>
//#include <linux/kfifo.h>
//#include <linux/log2.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kfifo.h"


static inline int is_power_of_2(unsigned long n)
{
    return (n != 0 && ((n & (n - 1)) == 0));
}

#define roundup_pow_of_two(size)    1       //????????????????????????????????????????????????

#define MAX_ERRNO   4095
#define unlikely(x) __builtin_expect(!!(x), 0)
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)

#define BUG()       printf("BUG at %s:%d!\n", __FILE__, __LINE__)
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0)

#define min(x, y) ({                        \
            typeof(x) _min1 = (x);          \
            typeof(y) _min2 = (y);          \
            (void) (&_min1 == &_min2);      \
            _min1 < _min2 ? _min1 : _min2; })


static inline void *ERR_PTR(long error)
{
        return (void *) error;
}

static inline long PTR_ERR(const void *ptr)
{
        return (long) ptr;
}

static inline long IS_ERR(const void *ptr)
{
        return IS_ERR_VALUE((unsigned long)ptr);
}


/**
 * kfifo_init - allocates a new FIFO using a preallocated buffer
 * @buffer: the preallocated buffer to be used.
 * @size: the size of the internal buffer, this have to be a power of 2.
 * @gfp_mask: get_free_pages mask, passed to kmalloc()
 * @lock: the lock to be used to protect the fifo buffer
 *
 * Do NOT pass the kfifo to kfifo_free() after use! Simply free the
 * &struct kfifo with kfree().
 */
struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
             gfp_t gfp_mask, spinlock_t *lock)
{
    struct kfifo *fifo;

    /* size must be a power of 2 */
    BUG_ON(!is_power_of_2(size));

    //fifo = kmalloc(sizeof(struct kfifo), gfp_mask);
    fifo = malloc(sizeof(struct kfifo));
    if (!fifo)
        return ERR_PTR(-ENOMEM);

    fifo->buffer = buffer;
    fifo->size = size;
    fifo->in = fifo->out = 0;
    fifo->lock = lock;

    return fifo;
}
//EXPORT_SYMBOL(kfifo_init);

/**
 * kfifo_alloc - allocates a new FIFO and its internal buffer
 * @size: the size of the internal buffer to be allocated.
 * @gfp_mask: get_free_pages mask, passed to kmalloc()
 * @lock: the lock to be used to protect the fifo buffer
 *
 * The size will be rounded-up to a power of 2.
 */
struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock)
{
    unsigned char *buffer;
    struct kfifo *ret;

    /*
     * round up to the next power of 2, since our 'let the indices
     * wrap' technique works only in this case.
     */
    if (!is_power_of_2(size)) {
        BUG_ON(size > 0x80000000);
#if 1
        size = roundup_pow_of_two(size);
#endif
    }
    //buffer = kmalloc(size, gfp_mask);
    buffer = malloc(size);
    if (!buffer)
        return ERR_PTR(-ENOMEM);

    ret = kfifo_init(buffer, size, gfp_mask, lock);

    if (IS_ERR(ret))
        free(buffer);
        //kfree(buffer);

    return ret;
}
//EXPORT_SYMBOL(kfifo_alloc);

/**
 * kfifo_free - frees the FIFO
 * @fifo: the fifo to be freed.
 */
void kfifo_free(struct kfifo *fifo)
{
    free(fifo->buffer);
    free(fifo);
    //kfree(fifo->buffer);
    //kfree(fifo);
}
//EXPORT_SYMBOL(kfifo_free);

/**
 * __kfifo_put - puts some data into the FIFO, no locking version
 * @fifo: the fifo to be used.
 * @buffer: the data to be added.
 * @len: the length of the data to be added.
 *
 * This function copies at most @len bytes from the @buffer into
 * the FIFO depending on the free space, and returns the number of
 * bytes copied.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these functions.
 */
unsigned int __kfifo_put(struct kfifo *fifo,
            const unsigned char *buffer, unsigned int len)
{
    unsigned int l;

    len = min(len, fifo->size - fifo->in + fifo->out);

    /*
     * Ensure that we sample the fifo->out index -before- we
     * start putting bytes into the kfifo.
     */

    //smp_mb();

    /* first put the data starting from fifo->in to buffer end */
    l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
    memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);

    /* then put the rest (if any) at the beginning of the buffer */
    memcpy(fifo->buffer, buffer + l, len - l);

    /*
     * Ensure that we add the bytes to the kfifo -before-
     * we update the fifo->in index.
     */

    //smp_wmb();

    fifo->in += len;

    return len;
}
//EXPORT_SYMBOL(__kfifo_put);

/**
 * __kfifo_get - gets some data from the FIFO, no locking version
 * @fifo: the fifo to be used.
 * @buffer: where the data must be copied.
 * @len: the size of the destination buffer.
 *
 * This function copies at most @len bytes from the FIFO into the
 * @buffer and returns the number of copied bytes.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these functions.
 */
unsigned int __kfifo_get(struct kfifo *fifo,
             unsigned char *buffer, unsigned int len)
{
    unsigned int l;

    len = min(len, fifo->in - fifo->out);

    /*
     * Ensure that we sample the fifo->in index -before- we
     * start removing bytes from the kfifo.
     */

    //smp_rmb();

    /* first get the data from fifo->out until the end of the buffer */
    l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
    memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);

    /* then get the rest (if any) from the beginning of the buffer */
    memcpy(buffer + l, fifo->buffer, len - l);

    /*
     * Ensure that we remove the bytes from the kfifo -before-
     * we update the fifo->out index.
     */

    //smp_mb();

    fifo->out += len;

    return len;
}
//EXPORT_SYMBOL(__kfifo_get);

kfifo.h

/*
 * A simple kernel FIFO implementation.
 *
 * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
#ifndef _LINUX_KFIFO_H
#define _LINUX_KFIFO_H

//#include <linux/kernel.h>
//#include <linux/spinlock.h>

#ifndef spinlock_t
#define spinlock_t  int
#endif

#ifndef gfp_t 
#define gfp_t   int
#endif


#ifndef spin_lock_init
#define spin_lock_init(x)
#endif

#ifndef spin_lock_irqsave
#define spin_lock_irqsave(x, y)
#endif

#ifndef spin_unlock_irqrestore
#define spin_unlock_irqrestore(x, y)
#endif

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 */
};

extern struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
                gfp_t gfp_mask, spinlock_t *lock);
extern struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask,
                 spinlock_t *lock);
extern void kfifo_free(struct kfifo *fifo);
extern unsigned int __kfifo_put(struct kfifo *fifo,
                const unsigned char *buffer, unsigned int len);
extern unsigned int __kfifo_get(struct kfifo *fifo,
                unsigned char *buffer, unsigned int len);

/**
 * __kfifo_reset - removes the entire FIFO contents, no locking version
 * @fifo: the fifo to be emptied.
 */
static inline void __kfifo_reset(struct kfifo *fifo)
{
    fifo->in = fifo->out = 0;
}

/**
 * kfifo_reset - removes the entire FIFO contents
 * @fifo: the fifo to be emptied.
 */
static inline void kfifo_reset(struct kfifo *fifo)
{
    unsigned long flags;

    spin_lock_irqsave(fifo->lock, flags);

    __kfifo_reset(fifo);

    spin_unlock_irqrestore(fifo->lock, flags);
}

/**
 * kfifo_put - puts some data into the FIFO
 * @fifo: the fifo to be used.
 * @buffer: the data to be added.
 * @len: the length of the data to be added.
 *
 * This function copies at most @len bytes from the @buffer into
 * the FIFO depending on the free space, and returns the number of
 * bytes copied.
 */
static inline unsigned int kfifo_put(struct kfifo *fifo,
                const unsigned char *buffer, unsigned int len)
{
    unsigned long flags;
    unsigned int ret;

    spin_lock_irqsave(fifo->lock, flags);

    ret = __kfifo_put(fifo, buffer, len);

    spin_unlock_irqrestore(fifo->lock, flags);

    return ret;
}

/**
 * kfifo_get - gets some data from the FIFO
 * @fifo: the fifo to be used.
 * @buffer: where the data must be copied.
 * @len: the size of the destination buffer.
 *
 * This function copies at most @len bytes from the FIFO into the
 * @buffer and returns the number of copied bytes.
 */
static inline unsigned int kfifo_get(struct kfifo *fifo,
                     unsigned char *buffer, unsigned int len)
{
    unsigned long flags;
    unsigned int ret;

    spin_lock_irqsave(fifo->lock, flags);

    ret = __kfifo_get(fifo, buffer, len);

    /*
     * optimization: if the FIFO is empty, set the indices to 0
     * so we don't wrap the next time
     */
    if (fifo->in == fifo->out)
        fifo->in = fifo->out = 0;

    spin_unlock_irqrestore(fifo->lock, flags);

    return ret;
}

/**
 * __kfifo_len - returns the number of bytes available in the FIFO, no locking version
 * @fifo: the fifo to be used.
 */
static inline unsigned int __kfifo_len(struct kfifo *fifo)
{
    return fifo->in - fifo->out;
}

/**
 * kfifo_len - returns the number of bytes available in the FIFO
 * @fifo: the fifo to be used.
 */
static inline unsigned int kfifo_len(struct kfifo *fifo)
{
    unsigned long flags;
    unsigned int ret;

    spin_lock_irqsave(fifo->lock, flags);

    ret = __kfifo_len(fifo);

    spin_unlock_irqrestore(fifo->lock, flags);

    return ret;
}

#endif

 

main.c

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kfifo.h"

struct KfifoNode {
    unsigned int num;
    char *string;
};

char *array[] = {
    "abcdefg",
    "gfedcba",
    "aaaaa",
    "bbbb",
    "ccc",
    "dd",
    "e",
    "12345",
    "1234",
    "123",
    "12",
    "1",
    "1111",
};

#define TAB_SIZE(array) (sizeof(array)/sizeof(array[0]))

static struct kfifo *pkfifoSpace;
static spinlock_t fifoLock; 

static void kfifo_check(char* str, int line, struct kfifo* pkfifo) 
{
    if(pkfifo != NULL) {
        printf("[%s-%d]: pkfifo->size = %-4d pkfifo->in = %-4d pkfifo->out = %-4d ", str, line, pkfifo->size, pkfifo->in, pkfifo->out);
    }
}


#ifndef GFP_KERNEL
#define GFP_KERNEL  1
#endif

int main(int argc, char* argv[])
{
    int i; 
    struct KfifoNode *pstNode;

    pkfifoSpace = kfifo_alloc((sizeof(struct KfifoNode) << 4), GFP_KERNEL, &fifoLock);
    if (pkfifoSpace == NULL) {
        printf("kfifo_alloc failed !\n");
        return -EFAULT;
    }
    spin_lock_init(&fifoLock); //Initial fifo spinlock 
    //pstNode = kzalloc(sizeof(struct KfifoNode), GFP_KERNEL);
    pstNode = malloc(sizeof(struct KfifoNode));
    printf("******insert*********************************************************************\n");
    kfifo_check((char *)__func__, __LINE__, pkfifoSpace);
    printf("\n");
    for(i = 0; i < TAB_SIZE(array); i++) {
        pstNode->num = i;
        pstNode->string = (char *)array[i];
        kfifo_put(pkfifoSpace, (unsigned char *)pstNode, sizeof(struct KfifoNode)); //将数据写入缓冲区
        kfifo_check((char *)__func__, __LINE__, pkfifoSpace);
        printf("Num: %-3d, Message: %s\n", pstNode->num, pstNode->string);
    }
    if(!kfifo_len(pkfifoSpace)) {
        printf("[%s-%d]: kfifo_len return 0, test failed !!! \n", __func__, __LINE__);
        kfifo_reset(pkfifoSpace);
        kfifo_free(pkfifoSpace);

        return -1;
    }

    printf("------get-and-display------------------------------------------------------------\n");
    //for(i = 0; i < TAB_SIZE(array); i++) {
    while(kfifo_len(pkfifoSpace)) {
        kfifo_get(pkfifoSpace, (unsigned char *)pstNode, sizeof(struct KfifoNode));
        kfifo_check((char *)__func__, __LINE__, pkfifoSpace);
        printf("Num is: %-3d, fifoMessage is: %s\n", pstNode->num, pstNode->string);
    }
    printf("------get-all-over----------------------------------------------------------------\n");
    kfifo_check((char *)__func__, __LINE__, pkfifoSpace);
    printf("\n--------------------------------------------------------------------------------\n");
    free(pstNode);
    kfifo_reset(pkfifoSpace);
    kfifo_free(pkfifoSpace);

    return  0;
}

 

编译链接运行, 输出如下:

 

转载于:https://www.cnblogs.com/zhanglong71/p/5183780.html

### kfifo 工作原理 kfifo 是一种基于环形缓冲区的第一入先出 (FIFO) 数据结构,在 Linux 内核中用于高效的数据传输[^3]。这种设计允许数据元素在被读取后无需移动其他元素的位置,减少了不必要的内存复制操作。 #### 环形缓冲区特性 通过使用环形缓冲区,kfifo 能够有效地管理固定大小的连续内存区域作为存储空间。每当写指针到达数组末端时会自动绕回到起始位置;同样地,读指针也会遵循相同的逻辑循环前进。这种方式不仅简化了边界条件处理,还提高了缓存命中率和整体性能表现[^1]。 #### 并发控制策略 对于单生产者/消费者场景下的无锁访问支持是 kfifo 的一大亮点。由于只有一个线程负责向队列添加新项而另一个仅限于移除现有条目,因此可以安全地省去互斥锁定机制,进而降低上下文切换开销并提升吞吐量。 ### 使用方法示例 下面是一个简单的例子展示了如何创建、初始化以及操作一个 kfifo 实例: ```c #include <linux/kfifo.h> #include <linux/slab.h> // 定义 FIFO 大小 #define BUFFER_SIZE 256 int main(void){ struct kfifo *my_fifo; // 动态分配一个新的 kfifo 结构体实例及其关联的内部缓冲区 my_fifo = kfifo_alloc(BUFFER_SIZE, GFP_KERNEL); if (!my_fifo) return -ENOMEM; unsigned char data[] = "test"; size_t bytes_written, bytes_read; char buffer[BUFFER_SIZE]; // 向 FIFO 中写入数据 __kfifo_put(my_fifo, data, sizeof(data), &bytes_written); // 从 FIFO 中读取数据 __kfifo_get(my_fifo, buffer, BUFFER_SIZE, &bytes_read); printk(KERN_INFO "Read %zu bytes from fifo\n", bytes_read); // 清理资源 kfifo_free(my_fifo); return 0; } ``` 上述代码片段说明了基本的操作流程:首先是 `kfifo_alloc` 函数用来申请指定容量的新对象;接着利用 `__kfifo_put` 和 `__kfifo_get` 方法分别执行写入与读取消息的任务;最后调用 `kfifo_free` 来释放之前所占用的空间。 ### 判空判断 为了方便开发者快速检查当前是否存在可读取的内容,提供了如下所示的宏定义来进行即时检测: ```c static inline __must_check int kfifo_is_empty(struct kfifo *fifo) { return fifo->in == fifo->out; } ``` 此函数返回真表示没有任何待处理的信息等待提取,反之则意味着至少有一条记录可供获取[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值