[ZLG-ARM] Linux设备驱动程序学习-Linux中的循环缓冲区

[复制链接]
2621|1
 楼主| billen 发表于 2009-6-4 16:35 | 显示全部楼层 |阅读模式
参考资料:《Linux内核中的循环缓冲区》作者:西邮&nbsp;王聪&nbsp;&nbsp;&nbsp;&nbsp;严重感谢**作者!&nbsp;但是(可能是源码版本问题)有些结论并不正确:&nbsp;“而kfifo_init只会接受一个已分配好空间的fifo-&gtbuffer,不能和kfifo_free搭配,用kfifo_init分配的kfifo只能用kfree释放。”&nbsp;阅读源码可以得出这样的结论:kfifo_init和kfifo_alloc分配的kfifo都能用kfree释放。已经用实验证实。<br />原文链接地址:&nbsp;http://www.kerneltravel.net/jiaoliu/kern-kfifo.html<br /><br />--------------------------------------------------------------------------------<br /><br />在学习到第十章&nbsp;中断处理&nbsp;时,其中的中断驱动的I/O需要使用缓冲区,我觉得与其自己实现一个缓冲区,不如利用内核已经写好的fifo。内核里有一个通用的循环缓冲区的实现在&nbsp;。<br />使用的数据结构如下:<br />struct&nbsp;kfifo&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;char&nbsp;*buffer;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;使用的缓冲区头指针&nbsp;*/<br />&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;int&nbsp;size;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;缓冲区总大小&nbsp;*/<br />&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;int&nbsp;in;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;已写入缓冲区的数据总量,当前缓冲区写指针的偏移量:(in&nbsp;%&nbsp;size)&nbsp;*/<br />&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;int&nbsp;out;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;已读出缓冲区的数据总量,当前缓冲区读指针的偏移量:(out&nbsp;%&nbsp;size)&nbsp;*/<br />&nbsp;&nbsp;&nbsp;&nbsp;spinlock_t&nbsp;*lock;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;为避免竞态的自旋锁&nbsp;*/<br />};/*当in==out时,缓冲区为空;当(in-out)==size时,缓冲区已满*/<br /><br /><br />kfifo提供的循环缓冲的部分函数分为2类:<br /><br />(1)以双下划线开头,没有使用自旋锁函数;<br /><br />(2)没有双下划线开头,需要额外加锁的情况下使用的函数。<br /><br />其实第二类只是在第一类的基础上进行加锁后,实际的代码如下:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;long&nbsp;flags;<br />&nbsp;&nbsp;&nbsp;&nbsp;spin_lock_irqsave(fifo-&gtlock,&nbsp;flags);<br />&nbsp;&nbsp;&nbsp;&nbsp;/*第一类函数*/<br />&nbsp;&nbsp;&nbsp;&nbsp;spin_unlock_irqrestore(fifo-&gtlock,&nbsp;flags);<br /><br /><br /><br />以下我按使用的顺序介绍每个函数的使用,部分函数源码在kernel/kfifo.c中定义,这些接口是经过精心构造的,可以小心地避免一些边界情况,原理其实很简单,建议去看源码弄清楚实现的原理,可以学到一些编程技巧。<br /><br />(0)声明循环缓冲数据结构指针&nbsp;struct&nbsp;kfifo&nbsp;*tekkamanfifo;<br /><br /><br /><br />(1)初始化循环缓冲结构体&nbsp;struct&nbsp;kfifo&nbsp;*kfifo_init(unsigned&nbsp;char&nbsp;*buffer,&nbsp;unsigned&nbsp;int&nbsp;size,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gfp_t&nbsp;gfp_mask,&nbsp;spinlock_t&nbsp;*lock);<br />/*调用kfifo_init必须保证size是2的整数次幂,而且buffer只接受一个已分配好空间的指针。也就是说之前要使用kmalloc分配好空间,将返回的指针传递到buffer*/<br />struct&nbsp;kfifo&nbsp;*kfifo_alloc(unsigned&nbsp;int&nbsp;size,&nbsp;gfp_t&nbsp;gfp_mask,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spinlock_t&nbsp;*lock);<br />/*调用kfifo_alloc不必保证size是2的幂,它内部会把size向上调整到2的整数次幂。空间分配的内部实现使用kmalloc。函数内部调用kfifo_init/<br /><br /><br /><br />buffer:之前要使用kmalloc分配好的空间指针;<br /><br />size:循环缓冲空间大小;<br /><br />gfp_mask:和kmalloc使用的分配标志(flags)一样。(参阅Linux设备驱动程序学习(8)-分配内存)<br /><br />lock:是事先声明并初始化好的自旋锁结构体指针;<br /><br />返回值&nbsp;为初始化好的循环缓冲数据结构指针&nbsp;。<br /><br />(2)&nbsp;向缓冲区里写入数据&nbsp;unsigned&nbsp;int&nbsp;kfifo_put(struct&nbsp;kfifo&nbsp;*fifo,unsigned&nbsp;char&nbsp;*buffer,&nbsp;unsigned&nbsp;int&nbsp;len);<br />unsigned&nbsp;int&nbsp;__kfifo_put(struct&nbsp;kfifo&nbsp;*fifo,unsigned&nbsp;char&nbsp;*buffer,&nbsp;unsigned&nbsp;int&nbsp;len);<br /><br /><br /><br />fifo:要写入数据的缓冲区结构体指针;<br /><br />buffer:要写入的数据指针,指向内核空间。如需要用户空间数据,之前要用copy_from_user复制数据到内核空间;<br /><br />len:要写入的数据大小;<br /><br />返回值&nbsp;为写入缓冲区的数据字节数。<br /><br />(3)从缓冲区里读出数据&nbsp;unsigned&nbsp;int&nbsp;kfifo_get(struct&nbsp;kfifo&nbsp;*fifo,&nbsp;unsigned&nbsp;char&nbsp;*buffer,&nbsp;unsigned&nbsp;int&nbsp;len);<br />unsigned&nbsp;int&nbsp;__kfifo_get(struct&nbsp;kfifo&nbsp;*fifo,&nbsp;unsigned&nbsp;char&nbsp;*buffer,&nbsp;unsigned&nbsp;int&nbsp;len);<br /><br /><br /><br />参数定义和kfifo_put类似。<br /><br />返回值&nbsp;为从缓冲区读出的数据字节数。<br /><br />(4)得到缓冲区已有的数据字节数&nbsp;unsigned&nbsp;int&nbsp;kfifo_len(struct&nbsp;kfifo&nbsp;*fifo);<br />unsigned&nbsp;int&nbsp;__kfifo_len(struct&nbsp;kfifo&nbsp;*fifo);<br /><br /><br /><br />fifo:要操作的缓冲区结构体指针;<br /><br />函数返回缓冲区实际已有的数据字节数,内部实现十分简单,就是in&nbsp;-&nbsp;out;<br /><br />返回值&nbsp;为缓冲区已有的数据字节数。<br /><br />(5)清空缓冲区&nbsp;void&nbsp;__kfifo_reset(struct&nbsp;kfifo&nbsp;*fifo);<br />void&nbsp;kfifo_reset(struct&nbsp;kfifo&nbsp;*fifo);<br /><br /><br /><br />内部实现十分简单,就是in&nbsp;=&nbsp;out&nbsp;=&nbsp;0。<br /><br />(6)使用结束,释放缓冲区。&nbsp;void&nbsp;kfifo_free(struct&nbsp;kfifo&nbsp;*fifo);<br /><br /><br /><br />所有的kfifo提供的循环缓冲的函数就是这些。在理解内部实现原理的基础上才能更好的使用它,所以再次建议阅读源码,因为源码很简单,但是很精巧。&nbsp;<br /><br /><br />--------------------------------------------------------------------------------<br /><br /><br />ARM9开发板实验<br /><br />实验模块源码:scull-kfifo<br /><br />测试程序源码:scull-kfifo-test<br /><br />实验现象:&nbsp;[Tekkaman2440@SBC2440V4]#cd&nbsp;/lib/modules/<br />[Tekkaman2440@SBC2440V4]#insmod&nbsp;scull_kfifo.ko<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/devices<br />Character&nbsp;devices:<br />&nbsp;&nbsp;1&nbsp;mem<br />&nbsp;&nbsp;2&nbsp;pty<br />&nbsp;&nbsp;3&nbsp;ttyp<br />&nbsp;&nbsp;4&nbsp;/dev/vc/0<br />&nbsp;&nbsp;4&nbsp;tty<br />&nbsp;&nbsp;4&nbsp;ttyS<br />&nbsp;&nbsp;5&nbsp;/dev/tty<br />&nbsp;&nbsp;5&nbsp;/dev/console<br />&nbsp;&nbsp;5&nbsp;/dev/ptmx<br />&nbsp;&nbsp;7&nbsp;vcs<br />10&nbsp;misc<br />13&nbsp;input<br />14&nbsp;sound<br />81&nbsp;video4linux<br />89&nbsp;i2c<br />90&nbsp;mtd<br />116&nbsp;alsa<br />128&nbsp;ptm<br />136&nbsp;pts<br />153&nbsp;spi<br />180&nbsp;usb<br />189&nbsp;usb_device<br />204&nbsp;s3c2410_serial<br />252&nbsp;scull_kfifo<br />253&nbsp;usb_endpoint<br />254&nbsp;rtc<br /><br />Block&nbsp;devices:<br />&nbsp;&nbsp;1&nbsp;ramdisk<br />256&nbsp;rfd<br />&nbsp;&nbsp;7&nbsp;loop<br />31&nbsp;mtdblock<br />93&nbsp;nftl<br />96&nbsp;inftl<br />179&nbsp;mmc<br />[Tekkaman2440@SBC2440V4]#mknod&nbsp;-m&nbsp;666&nbsp;/dev/scull_kfifo&nbsp;c&nbsp;252&nbsp;0<br />[Tekkaman2440@SBC2440V4]#echo&nbsp;1234567890&nbsp;&gt&nbsp;/dev/scull_kfifo<br />'sh'&nbsp;did&nbsp;write&nbsp;11&nbsp;bytes<br />[Tekkaman2440@SBC2440V4]#/tmp/scull_kfifo_test<br />scull_kfifo:&nbsp;the&nbsp;module&nbsp;can&nbsp;not&nbsp;lseek!<br />please&nbsp;input&nbsp;the&nbsp;command&nbsp;:1<br />scull_kfifo:&nbsp;ioctl&nbsp;SCULL_KFIFO_SIZE&nbsp;len=11<br />please&nbsp;input&nbsp;the&nbsp;command&nbsp;:2<br />scull_kfifo:&nbsp;SCULL_KFIFO_RESET&nbsp;code=0<br />please&nbsp;input&nbsp;the&nbsp;command&nbsp;:1<br />scull_kfifo:&nbsp;ioctl&nbsp;SCULL_KFIFO_SIZE&nbsp;len=0<br />please&nbsp;input&nbsp;the&nbsp;command&nbsp;:q<br />[Tekkaman2440@SBC2440V4]#echo&nbsp;123456789012345678901234567890&nbsp;&gt&nbsp;/dev/scull_kfifo&nbsp;<br /><br />'sh'&nbsp;did&nbsp;write&nbsp;31&nbsp;bytes<br />[Tekkaman2440@SBC2440V4]#echo&nbsp;123456789012345678901234567890&nbsp;&gt&nbsp;/dev/scull_kfifo&nbsp;<br /><br />'sh'&nbsp;did&nbsp;write&nbsp;31&nbsp;bytes<br />[Tekkaman2440@SBC2440V4]#echo&nbsp;1234567890&nbsp;&gt&nbsp;/dev/scull_kfifo<br /><br />'sh'&nbsp;did&nbsp;write&nbsp;2&nbsp;bytes<br />'sh'&nbsp;did&nbsp;write&nbsp;0&nbsp;bytes<br />'sh'&nbsp;did&nbsp;write&nbsp;0&nbsp;bytes<br />'sh'&nbsp;did&nbsp;write&nbsp;0&nbsp;bytes<br />'sh'&nbsp;did&nbsp;write&nbsp;0&nbsp;bytes<br />'sh'&nbsp;did&nbsp;write&nbsp;0&nbsp;bytes<br />'sh'&nbsp;did&nbsp;write&nbsp;0&nbsp;bytes<br />'sh'&nbsp;did&nbsp;write&nbsp;0&nbsp;bytes<br />'sh'&nbsp;did&nbsp;write&nbsp;0&nbsp;bytes<br />printk:&nbsp;204310&nbsp;messages&nbsp;suppressed.<br />'sh'&nbsp;did&nbsp;write&nbsp;0&nbsp;bytes<br /><br />1234567890<br />[Tekkaman2440@SBC2440V4]#/tmp/scull_kfifo_test<br />scull_kfifo:&nbsp;the&nbsp;module&nbsp;can&nbsp;not&nbsp;lseek!<br />please&nbsp;input&nbsp;the&nbsp;command&nbsp;:1<br />scull_kfifo:&nbsp;ioctl&nbsp;SCULL_KFIFO_SIZE&nbsp;len=64<br />please&nbsp;input&nbsp;the&nbsp;command&nbsp;:q<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/dev/scull_kfifo<br />printk:&nbsp;1493677&nbsp;messages&nbsp;suppressed.<br />'cat'&nbsp;did&nbsp;read&nbsp;64&nbsp;bytes<br />1234'cat'&nbsp;reading:&nbsp;going&nbsp;to&nbsp;sleep<br />56789012345678901234567890<br />123456789012345678901234567890<br />12<br />[Tekkaman2440@SBC2440V4]#/tmp/scull_kfifo_test<br />scull_kfifo:&nbsp;the&nbsp;module&nbsp;can&nbsp;not&nbsp;lseek!<br />please&nbsp;input&nbsp;the&nbsp;command&nbsp;:2<br />scull_kfifo:&nbsp;SCULL_KFIFO_RESET&nbsp;code=0<br />please&nbsp;input&nbsp;the&nbsp;command&nbsp;:q<br />[Tekkaman2440@SBC2440V4]#rmmod&nbsp;scull_kfifo<br />[Tekkaman2440@SBC2440V4]#lsmod<br />Module&nbsp;Size&nbsp;Used&nbsp;by&nbsp;Not&nbsp;tainted<br />[Tekkaman2440@SBC2440V4]#<br />&nbsp;<br /> &nbsp;&nbsp;<br />
armpc 发表于 2009-6-4 17:35 | 显示全部楼层

佩服

  
您需要登录后才可以回帖 登录 | 注册

本版积分规则

37

主题

53

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部