在学习到 并发和竞态 时,其中的提到了缓冲区,用于实现免锁算法,这里转载的是大神有关循环缓冲区做的一些操作。

其中源代码在最下面的附件中,有关作者的讲解感觉很清晰,很好,不过这里说一下自己的见解:

点击(此处)折叠或打开

/*

* Data management: read and write

*/

ssize_t scull_kfifo_read(struct file *filp, char __user *buf, size_t count,

loff_t *f_pos)

{

struct scull_kfifo *dev = filp->private_data;

ssize_t retval = 0;

if (down_interruptible(&dev->sem))

return -ERESTARTSYS;

// get the length of the fifo

while (!kfifo_len(dev->tekkamankfifo)) { /* nothing to read */

// release the lock

up(&dev->sem);

// check whether the filp can lock/sleep

if (filp->f_flags & O_NONBLOCK)

return -EAGAIN;

printk(""%s" reading: going to sleepn", current->comm);

// begin to sleep

if (wait_event_interruptible(dev->inq, kfifo_len(dev->tekkamankfifo)))

return -ERESTARTSYS; /* signal: tell the fs layer to handle it */

// after the sleep, go on to protect the deal(pay more attention here)

/* otherwise loop, but first reacquire the lock */

if (down_interruptible(&dev->sem))

return -ERESTARTSYS;

}

if (count > kfifo_len(dev->tekkamankfifo))

count = kfifo_len(dev->tekkamankfifo);

// read the data from the fifo

count = kfifo_get(dev->tekkamankfifo,tekkaman, count);

if (copy_to_user(buf, tekkaman, count)) {

retval = -EFAULT;

goto out;

}

retval = count;

out:

up(&dev->sem);

wake_up_interruptible(&dev->outq);

if (printk_ratelimit())     printk(""%s" did read %li bytesn",current->comm, (long)retval);

return retval;

}

ssize_t scull_kfifo_write(struct file *filp, const char __user *buf, size_t count,

loff_t *f_pos)

{

struct scull_kfifo *dev = filp->private_data;

ssize_t retval = -ENOMEM; /* value used in "goto out" statements */

if (down_interruptible(&dev->sem))

return -ERESTARTSYS;

if ( count>BUFSIZE ) count = BUFSIZE;

if (copy_from_user(tekkaman, buf, count)) {

up (&dev->sem);

return -EFAULT;

}

//write the data into the fifo

count = kfifo_put(dev->tekkamankfifo,tekkaman, count);

retval = count;

up(&dev->sem);

wake_up_interruptible(&dev->inq); /* blocked in read() and select() */

if (printk_ratelimit())

printk(""%s" did write %li bytesn",current->comm, (long)count);

return retval;

}

上面是修过修改的代码,做一下说明,其中最主要是在read和write函数中加入的信号量作为保护这里, 循环缓冲区本身就是免锁算法,

也就是说本身已经加上锁了,不知道这里大神为什么还是另外加入了一个信号量,无疑浪费了循环缓冲区尽量减少鎖機制的使用这个目的,

循环缓冲区采用生产者/消费者模式很好的实现了免锁算法,

以上为学习ldd3自己的见解.

内核里有一个通用的循环缓冲区的实现在 。

使用的数据结构如下:

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开发板实验

实验现象:

[ 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 循环缓冲区 源码,Linux中的循环缓冲区相关推荐

  1. linux下free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  2. linux c free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  3. linux 虚拟网卡 源码,Linux的虚拟网卡TUN和TAP

    TUN/TAP 提供了给用户空间程序的包的接收和传输,它可以看成是简单的点对点设备或是 以太网设备.它不是从物理设备接收包,而是从用户空间程序接收包.它发送包不是通过物 理设备来发送包,而是将这些包写 ...

  4. linux usb摄像头 源码,Linux USB摄像头驱动实现源码分析

    Spac5xx的实现是按照标准的USB VIDEO设备的驱动框架编写(其具体的驱动框架可参照/usr/src/linux/drivers/usb/usbvideo.c文件),整个源程序由四个主体部分组 ...

  5. linux内核io源码,Linux Kernel do_io_submit()函数整数溢出漏洞

    发布日期:2010-09-21 更新日期:2010-09-27 受影响系统: Linux kernel 2.6.x 不受影响系统: Linux kernel 2.6.36-rc4 描述: ------ ...

  6. 传奇游戏源码 Linux版本 传奇源码 Linux版 三端源码和搭建, 然后打包生成APP

    此源码牛逼拉萨, 因为鄙人玩了好一段时间, 故此搞篇文章记录下几个技术关键点 Linux架设教程 先决条件: CentOS 7 Nginx 1.8 mysql 5.6 php 5.6 建议使用 IP: ...

  7. linux运行geoserver源码,Linux 下Geoserver 的部署

    之前做的是在windows下的Geoserver openlayers 的部署开发 现在需求是将这套系统移植到Linux下,首先先介绍如何在 Linux下部署Geoserver 关于Geoserver ...

  8. linux声卡驱动源码,Linux声卡驱动移植和測试(示例代码)

    一.分析驱动程序,依据开发板改动代码 代码太长,就不贴了,几个注意点: 1. 查看开发板原理图和S3C2410的datasheet,UDA1341的L3MODE.L3DATA.L3CLOCK分别与S3 ...

  9. LINUX进程调度分析源码,Linux 实时调度(源码分析)

    为了弄清楚在多cpu系统中是如何实现实时调度的,先引入以下几个概念: cpu的状态: 我们知道,在linux系统中,任务的优先级为0~140. INVALID:(-1)该cpu不可用 IDLE(0): ...

最新文章

  1. [SD2.0大会]王坚:Data–centric Computing
  2. 异常处理——Logging initialized using configuration in jar:file:/usr/local/apache-hive-2.3.4/lib/hive-comm
  3. Wireshark(2):应用Wireshark观察基本网络协议
  4. java 集成kafka单机版 适配jdk1.8
  5. RS-232、RS-422与RS-485标准
  6. vi/vim的使用方法及常用的快捷键--vimer们的福音
  7. 动态规划——Palindrome Partitioning II
  8. coreldrawx4缩略图显示不出来_cdrx4无法显示缩略图怎么办?不显示缩略图解决方法...
  9. oracle imp 换表空间,imp导入切换表空间
  10. java抖音字符视频_代码生成抖音文字视频
  11. 踏上Oracle ebs的道路
  12. java.util.regex.PatternSyntaxException: Unclosed counted closure near index 14 [0-9a-zA-Z]{1, 20}报错
  13. python3 获取整分钟数的时间,如间隔半小时
  14. 树莓派2使用360wifi ,小米wifi
  15. 在IOS设备上调试(真机调试)
  16. python opencv 图像叠加,python opencv图像叠加/图像融合/mask掩模
  17. mysql查询集合查询之数据库除法、关系代数除法(优化嵌套查询)
  18. DICOM:DICOM万能编辑工具之Sante DICOM Editor
  19. php定位导航系统,jQuery实现定位导航位置详解
  20. 立体捕捉系统市场现状研究分析报告

热门文章

  1. 个人计算机网刻系统,全自动网刻后修改计算机配置信息(网管轻松方便)
  2. php输出一条直线,Photoshop脚本 绘制一条线
  3. php语言开始和结束分别为,0055 PHP语言简介和HelloWorld
  4. php pdo sql注入,「原创」PHP实战-PDO优化及 SQL注入
  5. 单独组件_iOS组件化/模块化的方案总结
  6. java如何画矩形条和填充_java.awt.Graphics 类的哪个方法可绘制填充矩形?
  7. mysql防注入插件_mybatis-plus插件使用的一些问题
  8. RAC中如何更改对外网卡和内部互联网卡的配置、IP及VIP
  9. 工具箱锁打不开了怎么办_木门门锁坏了怎么办?维修小技巧在手不发愁
  10. windows7中安装jdk1.8