免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 728 | 回复: 0
打印 上一主题 下一主题

Linux设备驱动程序学习(3-补)-Linux中的循环缓冲区 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-12-01 16:17 |只看该作者 |倒序浏览

参考资料:《Linux内核中的循环缓冲区》作者:西邮 王聪    严重感谢文章作者! 但是(可能是源码版本问题)有些结论并不正确: “而kfifo_init只会接受一个已分配好空间的fifo->buffer,不能和kfifo_free搭配,用kfifo_init分配的kfifo只能用kfree释放。” 阅读源码可以得出这样的结论:kfifo_init和kfifo_alloc分配的kfifo都能用kfree释放。已经用实验证实。
原文链接地址:
http://www.kerneltravel.net/jiaoliu/kern-kfifo.html
在学习到第十章 中断处理 时,其中的中断驱动的I/O需要使用缓冲区,我觉得与其自己实现一个缓冲区,不如利用内核已经写好的fifo。内核里有一个通用的循环缓冲区的实现在
使用的数据结构如下:
struct kfifo {
    unsigned char *buffer;    /* 使用的缓冲区头指针 */
    unsigned int size;    /* 缓冲区总大小 */
    unsigned int in;    /* 已写入缓冲区的数据总量,当前缓冲区写指针的偏移量:(in % size) */
    unsigned int out;    /* 已读出缓冲区的数据总量,当前缓冲区读指针的偏移量:(out % size) */
    spinlock_t *lock;    /* 为避免竞态的自旋锁 */
};/*当in==out时,缓冲区为空;当(in-out)==size时,缓冲区已满*/
kfifo提供的循环缓冲的部分函数分为2类:
(1)以双下划线开头,没有使用自旋锁函数;
(2)没有双下划线开头,需要额外加锁的情况下使用的函数。
其实第二类只是在第一类的基础上进行加锁后,实际的代码如下:
    unsigned long flags;
    spin_lock_irqsave(fifo->lock, flags);
    /*第一类函数*/
    spin_unlock_irqrestore(fifo->lock, flags);
以下我按使用的顺序介绍每个函数的使用,部分函数源码在kernel/kfifo.c中定义,这些接口是经过精心构造的,可以小心地避免一些边界情况,原理其实很简单,建议去看源码弄清楚实现的原理,可以学到一些编程技巧。
(0)声明循环缓冲数据结构指针
struct kfifo *tekkamanfifo;
(1)初始化循环缓冲结构体
struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
                gfp_t gfp_mask, spinlock_t *lock);
/*调用kfifo_init必须保证size是2的整数次幂,而且buffer只接受一个已分配好空间的指针。也就是说之前要使用kmalloc分配好空间,将返回的指针传递到buffer*/
struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask,
                 spinlock_t *lock);
/*调用kfifo_alloc不必保证size是2的幂,它内部会把size向上调整到2的整数次幂。空间分配的内部实现使用kmalloc。函数内部调用kfifo_init/
buffer:之前要使用kmalloc分配好的空间指针;
size:循环缓冲空间大小;
gfp_mask:和kmalloc使用的分配标志(flags)一样。(参阅
Linux设备驱动程序学习(8)-分配内存

lock:是事先声明并初始化好的自旋锁结构体指针;
返回值 为初始化好的循环缓冲数据结构指针 。
(2) 向缓冲区里写入数据
unsigned int kfifo_put(struct kfifo *fifo,unsigned char *buffer, unsigned int len);
unsigned int __kfifo_put(struct kfifo *fifo,unsigned char *buffer, unsigned int len);
fifo:要写入数据的缓冲区结构体指针;
buffer:要写入的数据指针,指向内核空间。如需要用户空间数据,之前要用copy_from_user复制数据到内核空间;
len:要写入的数据大小;
返回值 为写入缓冲区的数据字节数。
(3)从缓冲区里读出数据
unsigned int kfifo_get(struct kfifo *fifo, unsigned char *buffer, unsigned int len);
unsigned int __kfifo_get(struct kfifo *fifo, unsigned char *buffer, unsigned int len);
参数定义和kfifo_put类似。
返回值 为从缓冲区读出的数据字节数。
(4)得到缓冲区已有的数据字节数
unsigned int kfifo_len(struct kfifo *fifo);
unsigned int __kfifo_len(struct kfifo *fifo);
fifo:要操作的缓冲区结构体指针;
函数返回缓冲区实际已有的数据字节数,内部实现十分简单,就是in - out;
返回值 为缓冲区已有的数据字节数。
(5)清空缓冲区
void __kfifo_reset(struct kfifo *fifo);
void kfifo_reset(struct kfifo *fifo);
内部实现十分简单,就是in = out = 0。
(6)使用结束,释放缓冲区。
void kfifo_free(struct kfifo *fifo);
所有的kfifo提供的循环缓冲的函数就是这些。在理解内部实现原理的基础上才能更好的使用它,所以再次建议阅读源码,因为源码很简单,但是很精巧。
ARM9开发板实验
实验模块源码:
scull-kfifo

测试程序源码:
scull-kfifo-test

实验现象:
[Tekkaman2440@SBC2440V4]#cd /lib/modules/
[Tekkaman2440@SBC2440V4]#insmod scull_kfifo.ko
[Tekkaman2440@SBC2440V4]#cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
10 misc
13 input
14 sound
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
153 spi
180 usb
189 usb_device
204 s3c2410_serial
252 scull_kfifo
253 usb_endpoint
254 rtc
Block devices:
  1 ramdisk
256 rfd
  7 loop
31 mtdblock
93 nftl
96 inftl
179 mmc
[Tekkaman2440@SBC2440V4]#mknod -m 666 /dev/scull_kfifo c 252 0
[Tekkaman2440@SBC2440V4]#echo 1234567890 > /dev/scull_kfifo
"sh" did write 11 bytes
[Tekkaman2440@SBC2440V4]#/tmp/scull_kfifo_test
scull_kfifo: the module can not lseek!
please input the command :1
scull_kfifo: ioctl SCULL_KFIFO_SIZE len=11
please input the command :2
scull_kfifo: SCULL_KFIFO_RESET code=0
please input the command :1
scull_kfifo: ioctl SCULL_KFIFO_SIZE len=0
please input the command :q
[Tekkaman2440@SBC2440V4]#echo 123456789012345678901234567890 > /dev/scull_kfifo
"sh" did write 31 bytes
[Tekkaman2440@SBC2440V4]#echo 123456789012345678901234567890 > /dev/scull_kfifo
"sh" did write 31 bytes
[Tekkaman2440@SBC2440V4]#echo 1234567890 > /dev/scull_kfifo
"sh" did write 2 bytes
"sh" did write 0 bytes
"sh" did write 0 bytes
"sh" did write 0 bytes
"sh" did write 0 bytes
"sh" did write 0 bytes
"sh" did write 0 bytes
"sh" did write 0 bytes
"sh" did write 0 bytes
printk: 204310 messages suppressed.
"sh" did write 0 bytes
1234567890
[Tekkaman2440@SBC2440V4]#/tmp/scull_kfifo_test
scull_kfifo: the module can not lseek!
please input the command :1
scull_kfifo: ioctl SCULL_KFIFO_SIZE len=64
please input the command :q
[Tekkaman2440@SBC2440V4]#cat /dev/scull_kfifo
printk: 1493677 messages suppressed.
"cat" did read 64 bytes
1234"cat" reading: going to sleep
56789012345678901234567890
123456789012345678901234567890
12
[Tekkaman2440@SBC2440V4]#/tmp/scull_kfifo_test
scull_kfifo: the module can not lseek!
please input the command :2
scull_kfifo: SCULL_KFIFO_RESET code=0
please input the command :q
[Tekkaman2440@SBC2440V4]#rmmod scull_kfifo
[Tekkaman2440@SBC2440V4]#lsmod
Module Size Used by Not tainted
[Tekkaman2440@SBC2440V4]#


回目录 Linux设备驱动程序学习



本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/84216/showart_1674482.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP