kfifo的源代码主要是<linux/kfifo.h>和<kernel/kfifo.c>文件
kfifo结构定义:
struct kfifo {
unsigned char *buffer;/*保存数据的缓冲空间*/
unsigend int size;/*分配的空间大小*/
unsigned int in;/*入队数据的offset(in % size)*/
unsigned int out;/*出队数据的offset(out %size)*/
};
kfifo_alloc函数:
/**
* kfifo_alloc - 分配一个内部的buffer
* @fifo;赋予新buffer的fifo
* @size: 分配buffer的大小,必须是2的幂
* @gfp_mask: get_free_pages掩码,传递给kmalloc()
*
* 这个函数动态地分配一个新的内部buffer.
*
* size会四舍五入为2的幂.
* buffer由kfifo_free()来释放.
* 返回值:成功则为0,否则返error code.
*/
int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask)
{
unsigned char *buffer;
/*将size向上舍入为最近2的幂*/
if (!is_power_of_2(size)) {
BUG_ON(size > 0x80000000);
size = roundup_pws_of_two(size);
}
buffer = kmalloc(size, gfp_mask);
if (!buffer) {/*分配失败*/
_kfifo_init(fifo, NULL, 0);
return -ENOMEM;
}
_kfifo_init(fifo, buffer, size);/*初始化fifo成员*/
return 0;
}
_kfifo_init函数:
/*
* _kfifo_init - 初始化fifo结构体成员
*/
static void _kfifo_init(struct kfifo *fifo, void *buffer,
unsigned int size)
{
fifo->buffer = buffer;
fifo->size = size;
kfifo_reset(fifo);/*重置fifo的in和out为0*/
}
kfifo_reset函数:
/**
* kfifo_reset - 移除FIFO所有的内容
* @fifo:将被清空的fifo
*/
static inline void kfifo_reset(struct kfifo *fifo)
{
fifo->in = fifo->out = 0;
}
kfifo_free函数:
/*
* kfifo_free - 释放FIFO内部的buffer
* @fifo: 被释放的fifo
*/
void kfifo_free(struct kfifo *fifo)
{
kfree(fifo->buffer);
__kfifo_init(fifo, NULL, 0);
}
kfifo_init函数:
/*
* kfifo_init - 用一个已分配的buffer来初始化FIFO
*
* @fifo: 赋予buffer的fifo
* @buffer: 预先分配的buffer
* @size: buffer的大小,必须为2的幂
*/
void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size)
{
BUG_ON(!is_power_of_2(size));
_kfifo_init(fifo, buffer, size);
}
DECLARE_KFIFO宏:
#define DECLARE_KFIFO(name, size) \
union { \
struct kfifo name; \
unsigned char name##kfifo_buffer[size + sizeof(struct kfifo)]; \
}
INIT_KFIFO宏:
#define INIT_KFIFO(name) \
name = __kfifo_initializer(sizeof(name##kfifo_buffer) - \
sizeof(struct kfifo), \
name##kfifo_buffer + sizeof(struct kfifo))
__kfifo_initializer宏:
#define __kfifo_initializer(s, b) \
(struct kfifo) {
.size = s, \
.in = 0, \
.out = 0, \
.buffer = b \
}
这三个宏完成的功能示意图:
+---------------+<---name##kfifo_buffer
/ | | \
| | fifo.buffer | |
| | fifo.size | sizeof(struct kfifo)
| | fifo.in=0 | |
| | fifo.out=0 | /
| +---------------+<---fifo.buffer=name##kfifo_buffer+sizeof(struct kfifo)
| | | \
| | | |
size+siz| | |
of(struc| | |
t kfifo)| | fifo.size=sizeof(name##kfifo_buffer)-sizeof(struct kfifo)
| | | |
| | | |
| | | |
| | | |
| | | |
\ | | /
+---------------+
kfifo_init函数:
/**
* kfifo_in - 把数据放置到FIFO中
* @fifo: 存放数据的fifo
* @from: 数据来源
* @len: 数据的长度(字节)
*
* 从@from缓存中至多复制len字节的数据到@fifo中,且受限于@fifo的空闲空间.
* 返回值:已复制的数据长度(字节)
*/
unsigned int kfifo_in(struct kfifo *fifo, const void *from,
unsigned int len)
{
len = min(kfifo_avail(fifo), len);
__kfifo_in_data(fifo, from, len, 0);
__kfifo_add_in(fifo, len);
return len;
}
kfifo_avail函数:
/**
* kfifo_avail - 获取fifo的空闲空间字节数
*/
static inline unsigned int kfifo_avail(struct kfifo *fifo)
{
return kfifo_size(fifo) - kfifo_len(fifo);
}
kfifo_size函数:
/**
* kfifo_size - 返回fifo的总空间字节大小
* @fifo:
*/
static inline unsigned int kfifo_size(struct kfifo *fifo)
{
return fifo->size;
}
kfifo_len函数:
/**
* kfifo_len - 返回FIFO已使用空间的字节数
* @fifo:
*/
static inline unsigned int kfifo_len(struct kfifo *fifo)
{
register unsigned int out;
out = fifo->out;
smp_rmb();
return fifo->in - out;
}
__kfifo_in_data函数:
/**
* __kfifo_in_data - 从from缓存中将len字节的数据复制到从off开始位置的fifo中
* @off: 相对in的偏移量
*/
static inline void __kfifo_in_data(struct kfifo *fifo,
const void *from, unsigned int len, unsigned int off)
{
unsigned int l;
smp_mb();
off = __kfifo_off(fifo, fifo->in + off);
l = min(len, fifo->size - off);/*从偏移量到fifo buffer的尾部的空闲长度*/
memcpy(fifo->buffer + off, from, l);/*先填充尾部空闲*/
memcpy(fifo->buffer, from + l, len - l);/*再填充头部空闲*/
}
__kfifo_off函数:
/**
* __kfifo_off - 计算给定offset在fifo中的索引值
*/
static inline unsigned int __kfifo_off(struct kfifo *fifo, unsigned int off)
{
return off & (fifo->size - 1);/*等价于(off % fifi->size)*/
}
__kfifo_add_in函数:
/**
* __kfifo_add_in - 更新fifo的in值
*/
static inline void __kfifo_add_in(struct kfifo *fifo, unsigned int off)
{
smp_wmb();
fifo->in += off;
}
kfifo_out函数:
/**
* kfifo_out - 从FIFO取出一些数据
*
* @fifo:
* @to: 存放取出后的数据的地方
* @len: 目的缓存的大小
*
* 这个函数从@fifo最多复制@len字节数据到@to缓存中.
* 返回值:已复制的字节数.
*/
unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len)
{
len = min(kfifo_len(fifo), len);
__kfifo_out_data(fifo, to, len, 0);
__kfifo_add_out(fifo, len);
return len;
}
__kfifo_out_data函数:
/**
* __kfifo_out_data - 从@fifo复制len字节的数据到to缓存中
*
* @off: 相当out的偏移量
*/
static inline void __kfifo_out_data(struct kfifo *fifo,
void *to, unsigned int len, unsigned int off)
{
unsigned int l;
smp_rmb();
off = __kfifo_off(fifo, fifo->out + off);
l = min(len, fifo->size - off);
memcpy(to, fifo->buffer + off, l);/*先获取尾部的数据*/
memcpy(to + l, fifo->buffer, len - l);/*先获取头部的数据*/
}
__kfifo_add_out函数:
/**
* __kfifo_add_out - 更新fifo的out值
*/
static inline void __kfifo_add_out(struct kfifo *fifo,
unsigned int off)
{
smp_mb();
fifo->out += off;
}
kfifo_out_peek函数:
/**
* kfifo_out_peek - 从fifo复制一些数据,但并不移除它们
*
* @fifo:
* @to: 存储复制数据的目的缓存
* @len: 目的缓冲大小
* @offset:
*
* 该函数从fifo的offset开始位置复制len字节的数据到to缓存中
* 返回值:已复制的字节数
*/
unsigned int kfifo_out_peek(struct kfifo *fifo, void *to, unsigned int len,
unsigned int offset)
{
len = min(kfifo_len(fifo), len + offset);
__kfifo_out_data(fifo, to, len, offset);
return len;
}
这个函数与kfifo_out的不同在于没有更新fifo的out值
kfifo_is_empty函数:
/**
* kfifo_is_empty - 检测fifo是否为空
*/
static inline int kfifo_is_empty(struct kfifo *fifo)
{
return fifo->in == fifo->out;
}
kfifo_is_full函数:
/**
* kfifo_is_full - 检测fifo是否已满
*/
static inline int kfifo_is_full(struct kfifo *fifo)
{
return kfifo_len(fifo) == kfifo_size(fifo);
}
对in和out操作数据示意分析:
初始:
size=1024
in=out=0
len=in-out=0(已有数据)
avail=size-len=1024(空闲空间)
插入100字节数据:kfifo_in(fifo, from, 100)
size=1024
off=(更新之前的in%size)=(0%1024)=0(数据插入位置)
in=100
out=0
len=in-out=100
avail=size-len=924
再插入1000字节数据:kfifo_in(fifo, from, 1000)
size=1024
off=(更新之前的in%size)=100%1024=100(数据插入位置)
in=100+min(avail, 1000)=100+924=1024
out=0
len=in-out=1024
avail=size-len=0
获取300字节数据:kfifo_out(fifo, to, 300)
size=1024
off=(更新之前的out%size)=(0%1024)=0(数据获取位置)
in=1024
out=0+min(len,300)=300
len=in-out=724
avail=size-len=300
再插入100字节数据:
size=1024
off=(更新之间的in%size)=1024%1024=0(数据插入位置)
in=1124
out=300
len=in-out=824
avail=size-len=200
获取800字节数据:
size=1024
off=(更新之前的300%size)=(300%1024)=300
in=1124
out=300+min(len,800)=1100
len=in-out=24
avail=size-len=1000
插入20字节数据:
size=1024
off=(更新之前的1124%size)=(1124%1024)=100
in=1144
out=1100
len=in-out=44
avail=size-len=980
相关文章
- linux内核源码解析02--启动代码分析之固定映射
- linux内核源码解析01–启动代码分析之汇编部分
- linux源码分析之字节序(2)-- types.h
- Linux0.11内核--进程调度分析之2.调度
- Linux内核(2) - 分析内核源码如何入手(上)
- Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】
- Linux内核源码分析--内核启动之(1)zImage自解压过程(Linux-3.0 ARMv7)
- Linux内核(2) - 分析内核源码如何入手(上)
- Linux内核---14.启动分析2之arch/arm/kernel/head.S
- Linux内核源码分析--内核启动之(2)Image内核启动(汇编部分)(Linux-3.0 ARMv7) 【转】