Linux内核中bytestream kfifo学习

本文详细介绍了kfifo数据结构,包括普通kfifo和kfifo_rec的区别,以及如何通过kfifo_alloc动态分配内存或kfifo_init预设缓存。此外,还讲解了kfifo的初始化、入队与出队方法,以及自旋锁在kfifo操作中的使用。
摘要由CSDN通过智能技术生成

目录

kfifo说明

kfifo的优点

 定义kfifo结构体

 kfifo初始化方法一

 kfifo初始化方法二

释放kfifo

kfifo入队方法一

kfifo入队方法二

kfifo出队方法一

kfifo出队方法二


kfifo说明

kfifo分为两种

        1、普通的kfifo,类似于普通的循环队列,每一个buff空间都用来存放数据

datadatadatadatadatadatadatadata

适用范围:流数据的存储,一个线程入队,一个线程出队

        2、带有长度记录的kfifo_rec,与kfifo不同的是,每次入队时都会在本次数据之前的1/2个字节来表示本次数据的长度。

len1data1data1data1data1len2data2data2

适用范围:块数据的数据缓存,操作单位是块

关于自旋锁的官方说明:

 ①单个读写操作不用上锁

 ②不调用kfifo_reset()不上锁

 ③调用kfifo_reset_out(),只需出队是上锁即可

 ④对于多个入队对应一个出队时,只需入队线程加锁即可

 ⑤对于多个出队对应一个入队时,只需出队线程加锁即可

kfifo的优点

实现了简单高效的队列操作。

 定义kfifo结构体

#define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \
	union { \
		struct __kfifo	kfifo; \
		datatype	*type; \
		const datatype	*const_type; \
		char		(*rectype)[recsize]; \
		ptrtype		*ptr; \
		ptrtype const	*ptr_const; \
	}
#define __STRUCT_KFIFO_PTR(type, recsize, ptrtype) \
{ \
	__STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
	type		buf[0]; \
}
/*
 * define compatibility "struct kfifo" for dynamic allocated fifos
 */
struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0, void);
//将上面两个宏定义展开可得:
struct kfifo{
	union { 
		struct __kfifo	kfifo; 
		datatype	*type; //传进来的是unsigned char类型
		const datatype	*const_type; 
		char		(*rectype)[recsize]; //对于普通的kfifo recsize为0, kfiro_rec recsize大小为1或2
		ptrtype		*ptr; //传进来的是void类型
		ptrtype const	*ptr_const; 
	}  
    unsigned char buf[0];
}
/*其中struct __kfifo:*/
struct __kfifo {
	unsigned int	in;		//队尾下标,入队列的offset为(in % size 或者 in & mask)
	unsigned int	out;    //队首下标,出队列的offset为(out % size 或者 out & mask)
	unsigned int	mask;   //缓冲区元素个数(size) - 1;使用&mask,替换%size,提升效率
	unsigned int	esize;  //缓存区每个元素的size(element size)
	void		*data; 		//用于存放数据的缓存区
};

 kfifo初始化方法一

//kfifo初始化 方法1
/**
 * kfifo_alloc - 动态分配一个新的fifo缓冲区
 * @fifo: pointer to the fifo
 * @size: the number of elements in the fifo, this must be a power of 2
 * @gfp_mask: get_free_pages mask, passed to kmalloc()
 *
 * The numer of elements will be rounded-up to a power of 2.
 * The fifo will be release with kfifo_free().
 * Return 0 if no error, otherwise an error code.
 */
#define kfifo_alloc(fifo, size, gfp_mask) \
__kfifo_int_must_check_helper( \
({ \
	typeof((fifo) + 1) __tmp = (fifo); \
	struct __kfifo *__kfifo = &__tmp->kfifo; \
	__is_kfifo_ptr(__tmp) ? \
	__kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : \
	-EINVAL; \
}) \
)
//typeof()是C语言的关键字,用于获取表达式的类型
//fifo的类型为struct kfifo*,fifo+1根据类型转换还是指针类型,typeof((fifo)+1)就是struct kfifo*
/*指针+1的操作,相当于取同类型下一个元素的地址,所以只有是指针才能正确,防止传进来的是数组,因此typeof(x + 1)主要是用来检查传进来的参数确保是指针 
参考文章:http://blog.chinaunix.net/uid-9672747-id-4045550.html*/

//将宏展开:
#define kfifo_alloc(fifo,size,gfp_mask)
{
    struct kfifo* __tmp = (fifo);
    struct __kfifo* __kfifo = &__tmp->kfifo;
    return __is_kfifo_ptr(__tmp) ? __kfifo_alloc(__kfifo,size,sizeof(*__tmp->type),gfp_mask): -EINVAL;
    //EINVAL    #define	   EINVAL		22	/* Invalid argument */
    //#define	__is_kfifo_ptr(fifo)	(sizeof(*fifo) == sizeof(struct __kfifo)) 判断kfifo是不是普通的kfifo
}
//调用__kfifo_alloc()
int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
		size_t esize, gfp_t gfp_mask)
{
	/*
	 * round down to the next power of 2, since our 'let the indices wrap' technique works only in this case.
	 * 向下四舍五入到2的下一次方,因为我们的“让索引包起来”技术只适用于这种情况。
	 */
	size = roundup_pow_of_two(size);

	fifo->in = 0;
	fifo->out = 0;
	fifo->esize = esize;

	if (size < 2) {
		fifo->data = NULL;
		fifo->mask = 0;
		return -EINVAL;
	}

	fifo->data = kmalloc(size * esize, gfp_mask);

	if (!fifo->data) {
		fifo->mask = 0;
		return -ENOMEM;
	}
	fifo->mask = size - 1;

	return 0;
}

 kfifo初始化方法二

//kfifo 初始化 方法二 与 kfifo_alloc的区别就在于一个是动态申请内存,一个是静态设置缓存
/**
 * kfifo_init - 使用预分配的缓冲区初始化fifo
 * @fifo: the fifo to assign the buffer
 * @buffer: the preallocated buffer to be used
 * @size: the size of the internal buffer, this have to be a power of 2
 */
#define kfifo_init(fifo, buffer, size) \
({ \
	typeof((fifo) + 1) __tmp = (fifo); \
	struct __kfifo *__kfifo = &__tmp->kfifo; \
	__is_kfifo_ptr(__tmp) ? \
	__kfifo_init(__kfifo, buffer, size, sizeof(*__tmp->type)) : \
	-EINVAL; \
})
//宏展开
#define kfifo_init(fifo, buffer, size) 
{ 
	struct kfifo*  __tmp = (fifo); 
	struct __kfifo *__kfifo = &__tmp->kfifo; 
	__is_kfifo_ptr(__tmp) ? __kfifo_init(__kfifo, buffer, size, sizeof(*__tmp->type)) : -EINVAL;
}
//调用__kfifo_init()
int __kfifo_init(struct __kfifo *fifo, void *buffer,
		unsigned int size, size_t esize)
{
	size /= esize;

	size = roundup_pow_of_two(size);

	fifo->in = 0;
	fifo->out = 0;
	fifo->esize = esize;
	fifo->data = buffer;

	if (size < 2) {
		fifo->mask = 0;
		return -EINVAL;
	}
	fifo->mask = size - 1;

	return 0;
}

函数名功能
kfifo_alloc动态申请缓存
kfifo_init预先设置buff缓存

释放kfifo

//释放kfifo
/**
 * kfifo_free - frees the fifo
 * @fifo: the fifo to be freed
 */
#define kfifo_free(fifo) \
({ \
	typeof((fifo) + 1) __tmp = (fifo); \
	struct __kfifo *__kfifo = &__tmp->kfifo; \
	if (__is_kfifo_ptr(__tmp)) \
		__kfifo_free(__kfifo); \
})
//宏展开
#define kfifo_free(fifo)
{
    struct kfifo* __tmp = (fifo);
    struct __kfifo* __kfifo = &_tmp->kfifo;
    if(__is_kfifo_ptr(__tmp))
        __kfifo_free(__kfifo);
}
//调用__kfifo_free()
void __kfifo_free(struct __kfifo *fifo)
{
	kfree(fifo->data);
	fifo->in = 0;
	fifo->out = 0;
	fifo->esize = 0;
	fifo->data = NULL;
	fifo->mask = 0;
}

kfifo入队方法一

//入队1
/**
 * kfifo_in - 入队
 * @fifo: address of the fifo to be used
 * @buf: the data to be added
 * @n: number of elements to be added
 *
 * This macro copies the given buffer into the fifo and returns the
 * number of copied elements.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these macro.
 */
#define	kfifo_in(fifo, buf, n) \
({ \
	typeof((fifo) + 1) __tmp = (fifo); \
	typeof(__tmp->ptr_const) __buf = (buf); \
	unsigned long __n = (n); \
	const size_t __recsize = sizeof(*__tmp->rectype); \
	struct __kfifo *__kfifo = &__tmp->kfifo; \
	(__recsize) ?\
	__kfifo_in_r(__kfifo, __buf, __n, __recsize) : \
	__kfifo_in(__kfifo, __buf, __n); \
})
//宏展开
#define	kfifo_in(fifo, buf, n) 
{ 
	struct kfifo* __tmp = (fifo); 
	void const * __buf = (buf); 
	unsigned long __n = (n); 
	const size_t __recsize = sizeof(*__tmp->rectype);  //这里__resize = 0;
	struct __kfifo *__kfifo = &__tmp->kfifo; 
	(__recsize) ?__kfifo_in_r(__kfifo, __buf, __n, __recsize) : __kfifo_in(__kfifo, __buf, __n); 
    //kfiro_rec 调用__kfifo_in_r(__kfifo, __buf, __n, __recsize)
    //kfiro 调用__kfifo_in(__kfifo, __buf, __n)
}
//调用__kfifo_in()
unsigned int __kfifo_in(struct __kfifo *fifo,
		const void *buf, unsigned int len)
{
	unsigned int l;

	l = kfifo_unused(fifo);//获取未使用的缓存长度
    
	if (len > l)
		len = l;

	kfifo_copy_in(fifo, buf, len, fifo->in);
	fifo->in += len;
	return len;
}
/*获取kfifo未使用的缓存长度*/
static inline unsigned int kfifo_unused(struct __kfifo *fifo)
{
	return (fifo->mask + 1) - (fifo->in - fifo->out);
}
/**/
static void kfifo_copy_in(struct __kfifo *fifo, const void *src,
		unsigned int len, unsigned int off)
{
	unsigned int size = fifo->mask + 1;
	unsigned int esize = fifo->esize;
	unsigned int l;

	off &= fifo->mask;//与运算代替取余运算效率更高  入队列的offset为(in % size 或者 in & mask) 获取到 fifo->in 在内存 buffer 中的对应位置
	if (esize != 1) {
		off *= esize;
		size *= esize;
		len *= esize;
	}
      /*
     *拷贝数据到 in 后面的内存
     *如果 in 后面的内存 buffer 段不够存放
     *则将剩余的数据拷贝到内存 buffer 的开头对应的位置
     */
	l = min(len, size - off);

	memcpy(fifo->data + off, src, l);
	memcpy(fifo->data, src + l, len - l);
	/*
	 * make sure that the data in the fifo is up to date before
	 * incrementing the fifo->in index counter
	 */
	smp_wmb();//内存屏障 #define smp_wmb()	barrier() static inline void barrier(void){	asm volatile("" : : : "memory");}
}

kfifo入队方法二

//入队2
/**
 * kfifo_in_spinlocked - 使用自旋锁将数据放入fifo进行锁定
 * @fifo: address of the fifo to be used
 * @buf: the data to be added
 * @n: number of elements to be added
 * @lock: pointer to the spinlock to use for locking
 *
 * This macro copies the given values buffer into the fifo and returns the
 * number of copied elements.
 */
#define	kfifo_in_spinlocked(fifo, buf, n, lock) \
({ \
	unsigned long __flags; \
	unsigned int __ret; \
	spin_lock_irqsave(lock, __flags); \
	__ret = kfifo_in(fifo, buf, n); \
	spin_unlock_irqrestore(lock, __flags); \
	__ret; \
})
//宏展开
#define	kfifo_in_spinlocked(fifo, buf, n, lock)
{ 
	unsigned long __flags; 
	unsigned int __ret; 
	spin_lock_irqsave(lock, __flags); //自旋锁在任何情况下使用spin_lock_irq都是安全的。因为它既禁止本地中断,又禁止内核抢占
	__ret = kfifo_in(fifo, buf, n); 
	spin_unlock_irqrestore(lock, __flags); 
	return __ret; 
})
函数/宏定义功能

#define kfifo_put(fifo, val)

单字节入队

#define kfifo_in(fifo, buf, n)

多字节入队

#define kfifo_in_spinlocked(fifo, buf, n, lock)

入队时上锁

kfifo出队方法一

//出队
/**
 * kfifo_out - 将数据从fifo中出队
 * @fifo: address of the fifo to be used
 * @buf: pointer to the storage buffer
 * @n: max. number of elements to get
 *
 * This macro get some data from the fifo and return the numbers of elements
 * copied.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these macro.
 */
#define	kfifo_out(fifo, buf, n) \
__kfifo_uint_must_check_helper( \
({ \
	typeof((fifo) + 1) __tmp = (fifo); \
	typeof(__tmp->ptr) __buf = (buf); \
	unsigned long __n = (n); \
	const size_t __recsize = sizeof(*__tmp->rectype); \
	struct __kfifo *__kfifo = &__tmp->kfifo; \
	(__recsize) ?\
	__kfifo_out_r(__kfifo, __buf, __n, __recsize) : \
	__kfifo_out(__kfifo, __buf, __n); \
}) \
)
//宏展开
#define	kfifo_out(fifo, buf, n) 
{
    struct kfifo* __tmp = (fifo);
    void* __buf = (buf);
    unsigned long __n = (n);
    const size_t __recsize = sizeof(*__tmp->resctype);//此时为0
    struct __kfifo *__kfifo = &__tmp->kfifo; 
    (__recsize) ? __kfifo_out_r(__kfifo, __buf, __n, __recsize) : __kfifo_out(__kfifo, __buf, __n); 
}
//调用 __kfifo_out()
unsigned int __kfifo_out(struct __kfifo *fifo,
		void *buf, unsigned int len)
{
	len = __kfifo_out_peek(fifo, buf, len);
	fifo->out += len;
	return len;
}

kfifo出队方法二

//出队2
/**
 * kfifo_out_spinlocked - 使用自旋锁从fifo获取数据进行锁定
 * @fifo: address of the fifo to be used
 * @buf: pointer to the storage buffer
 * @n: max. number of elements to get
 * @lock: pointer to the spinlock to use for locking
 *
 * This macro get the data from the fifo and return the numbers of elements
 * copied.
 */
#define	kfifo_out_spinlocked(fifo, buf, n, lock) \
__kfifo_uint_must_check_helper( \
({ \
	unsigned long __flags; \
	unsigned int __ret; \
	spin_lock_irqsave(lock, __flags); \
	__ret = kfifo_out(fifo, buf, n); \
	spin_unlock_irqrestore(lock, __flags); \
	__ret; \
}) \
)
//宏展开
#define	kfifo_out_spinlocked(fifo, buf, n, lock) 
{ 
	unsigned long __flags; 
	unsigned int __ret; 
	spin_lock_irqsave(lock, __flags); //自旋锁
	__ret = kfifo_out(fifo, buf, n); 
	spin_unlock_irqrestore(lock, __flags); 
	__ret; 
} 
函数/宏定义

#define kfifo_get(fifo, val)

#define kfifo_peek(fifo, val)

#define kfifo_out(fifo, buf, n)

kfifo_out_peek(fifo, buf, n)
#define    kfifo_out_spinlocked(fifo, buf, n, lock) 

参考:https://blog.csdn.net/Bruno_Mars/article/details/100061793

           http://blog.chinaunix.net/uid-9672747-id-4045550.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值