linux device driver —— 环形缓冲区的实现
还是没有接触到怎么控制硬件,但是在书里看到了一个挺巧妙的环形缓冲区实现。
此环形缓冲区实际为一个大小为bufsize的一维数组,有一个rp的读指针,一个wp的写指针。
在数据满时写进程会等待读进程读取数据,数据为空时读进程会等待写进程写入数据。
在上次代码上改的,所以名字还是ioctldemo
ioctldemo.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/stat.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include <asm-generic/uaccess.h>
#include <asm-generic/ioctl.h>
#include <asm-generic/current.h>
#define IOCTLDEMO_MAJOR 0
#define MODULE_NAME "ioctldemo"
#define DEMO_MAGIC 'm'
#define DEMO_SIZE int
#define DEMO_NR_MAX 1
#define MY_IOCTL_READ _IOR(DEMO_MAGIC,1,DEMO_SIZE);
static int ioctldemo_major = IOCTLDEMO_MAJOR;
void ioctldemo_exit(void);
int ioctldemo_init(void);
long my_unlocked_ioctl(struct file*, unsigned int, unsigned long);
int my_cdev_open(struct inode*, struct file*);
int my_cdev_release(struct inode*,struct file*);
ssize_t my_cdev_read (struct file*, char __user*, size_t, loff_t *);
ssize_t my_cdev_write(struct file*, const char __user*, size_t, loff_t *);
MODULE_LICENSE("Dual BSD/GPL");
module_param(ioctldemo_major,int,S_IRUGO);
module_init(ioctldemo_init);
module_exit(ioctldemo_exit);
struct dev_driver
{
wait_queue_head_t inq,outq; //read and write queues
char *buffer, *end; //begin of buff, end of buff
int bufsize;//为了方便取余,必须为2的n次幂
char *rp,*wp;
struct semaphore sem;
struct cdev my_cdev;
}my_driver;
static struct file_operations cdev_ops =
{
.owner = THIS_MODULE,
.open = my_cdev_open,
.release = my_cdev_release,
.read = my_cdev_read,
.write = my_cdev_write,
};
int __init ioctldemo_init(void)
{
int ret;
dev_t devno;
printk(KERN_NOTICE "=== ioctldemo_init start\n");
devno = MKDEV(ioctldemo_major,);
if(ioctldemo_major)
{
printk(KERN_NOTICE "=== ioctldemo_init try register\n");
ret = register_chrdev_region(devno,,MODULE_NAME);
}else
{
printk(KERN_NOTICE "=== ioctldemo_init auto register\n");
ret = alloc_chrdev_region(&devno,,,MODULE_NAME);
ioctldemo_major = MAJOR(devno);
}
)
{
printk(KERN_NOTICE "=== ioctldemo_init register fail\n");
return ret;
}
cdev_init(&my_driver.my_cdev,&cdev_ops);
my_driver.my_cdev.owner = THIS_MODULE;
ret = cdev_add(&my_driver.my_cdev,MKDEV(ioctldemo_major,),);
)
{
printk(KERN_NOTICE "=== ioctldemo_init add cdev fail\n");
return ret;
}
//init buffer
my_driver.bufsize= <<;
my_driver.buffer = (char*)kmalloc(my_driver.bufsize,GFP_KERNEL);
my_driver.end = my_driver.buffer + my_driver.bufsize;
my_driver.rp = my_driver.wp = my_driver.buffer;
printk(KERN_DEBUG "ioctldemo buf->%p, end->%p",my_driver.buffer,my_driver.end);
//init semaphore
sema_init(&my_driver.sem,);
//init wait queue
init_waitqueue_head(&my_driver.inq);
init_waitqueue_head(&my_driver.outq);
printk(KERN_NOTICE "=== ioctldemo_init finish\n");
;
}
void __exit ioctldemo_exit(void)
{
printk (KERN_NOTICE "=== ioctldemo_exit");
kfree(my_driver.buffer);
cdev_del(&my_driver.my_cdev);
unregister_chrdev_region(MKDEV(ioctldemo_major,),);
}
int my_cdev_open(struct inode *node, struct file *filp)
{
;
}
int my_cdev_release(struct inode *node, struct file *filp)
{
;
}
/*一个环形缓冲区的不覆盖读写实现。
*对于 read函数: 写指针和读指针重合时视为数据为空,等待输入数据。每次读取最多读到缓冲区数组尾部。
*对于write函数: 每次写入最多写入到(读指针-1)位置或缓冲区数组尾部,如果写指针在读指针的前一项(相对环形来说)视为队列已满,等待读取数据。
*/
ssize_t my_cdev_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
if(down_interruptible(&my_driver.sem))
return -ERESTARTSYS;
while(my_driver.rp == my_driver.wp)
{
up(&my_driver.sem);
if(filp->f_flags == O_NONBLOCK)
return -EAGAIN;
printk(KERN_NOTICE "%s :reading, go to sleep\n",current->comm);
if(wait_event_interruptible(my_driver.inq,(my_driver.rp != my_driver.wp)))
return -ERESTARTSYS;
if(down_interruptible(&my_driver.sem))
return -ERESTARTSYS;
printk(KERN_DEBUG "%s :read awoken from waiting,rp:%p, rp:%p\n",current->comm,my_driver.rp,my_driver.wp);
}
if(my_driver.wp > my_driver.rp)
count = min(count,(size_t)(my_driver.wp - my_driver.rp));
else
count = min(count,(size_t)(my_driver.end - my_driver.rp));
if(copy_to_user(buf,my_driver.rp,count))
{
up(&my_driver.sem);
return -EFAULT;
}
my_driver.rp += count;
if(my_driver.rp == my_driver.end)
{
my_driver.rp = my_driver.buffer;
printk(KERN_DEBUG "ioctldemo convert rp");
}
up(&my_driver.sem);
wake_up_interruptible(&my_driver.outq);
printk(KERN_NOTICE "%s did read %ld bytes\n",current->comm,count);
return count;
}
//get max space that is free now
size_t my_cdev_getfree(struct dev_driver *drp)
{
if(drp->rp == drp->wp)
;
)) - ;
}
ssize_t my_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
if(down_interruptible(&my_driver.sem))
return -ERESTARTSYS;
)
{
up(&my_driver.sem);
if(filp->f_flags == O_NONBLOCK)
return -EAGAIN;
printk(KERN_NOTICE "%s :write go to sleep\n",current->comm);
)))
return -ERESTARTSYS;
if(down_interruptible(&my_driver.sem))
return -ERESTARTSYS;
printk(KERN_NOTICE "%s :write awoken from sleep\n",current->comm);
}
count = min(count,my_cdev_getfree(&my_driver));
if(my_driver.wp >= my_driver.rp)
count = min(count,(size_t)(my_driver.end - my_driver.wp));
else// writer pointer has wrapped, fill up to rp-1
count = min(count,(size_t)(my_driver.rp - my_driver.wp -));
if(copy_from_user(my_driver.wp,buf,count))
{
up(&my_driver.sem);
return -EFAULT;
}
my_driver.wp += count;
if(my_driver.wp == my_driver.end)
{
my_driver.wp = my_driver.buffer;
printk(KERN_DEBUG "ioctldemo convert wp");
}
up(&my_driver.sem);
wake_up_interruptible(&my_driver.inq);
printk(KERN_NOTICE "%s did write %ld bytes\n",current->comm,count);
return count;
}
编译,安装,卸载脚本见
测试:
例如我把驱动挂载到了/dev/ioctldemo
可以执行以下命令
cat /dev/ioctldemo
此时缓冲区因为没有数据,所以等待输入。
再打开另外一个终端,输入命令
ls > /dev/ioctldemo
可以看到之前的窗口显示出来了数据。
linux device driver —— 环形缓冲区的实现的更多相关文章
- Linux Device Driver 学习(1)
Linux Device Driver 学习(1) 一.搭建虚拟机开发环境 1.选择虚拟机VirtualBox,官网下载.deb包安装: VirtualBox Linux 5.1.6 下载fedora ...
- how to write your first linux device driver
how to write your first linux device driver 0. environment-ubuntu 1804 64bit 1. apt-get install linu ...
- Linux Device Driver && Device File
catalog . 设备驱动程序简介 . I/O体系结构 . 访问设备 . 与文件系统关联 . 字符设备操作 . 块设备操作 . 资源分配 . 总线系统 1. 设备驱动程序简介 设备驱动程序是内核的关 ...
- How to learn linux device driver
To learn device driver development, like any other new knowledge, the bestapproach for me is to lear ...
- <<linux device driver,third edition>> Chapter 4:Debugging Techniques
Debugging by Printing printk lets you classify messages accoring to their severity by associating di ...
- <<linux device driver,third edition>> Chapter 3:Char Drivers
The Internal Representation of Device Numbers Within the kernel,the dev_t type(defined in linux/type ...
- linux device driver —— ioctl
实现了应用程序和设备驱动通过ioctl通信.还是对设备驱动没什么感觉,贴一下代码吧. 在Ubuntu 16.04 64bit中测试通过 ioctldemo.c #include <linux/m ...
- linux device driver —— 字符设备
现在对linux设备驱动还没有什么认识,跟着书上敲了一个字符驱动,这里把代码贴一下. 测试环境是 Ubuntu 16.04 64bit 驱动程序: #include <linux/fs.h> ...
- <<linux device driver,third edition>> Chapter 2: Building and Running Modules
Kernel Modules Versus Applications Kernel modules programming is similar to event driven programming ...
随机推荐
- js中对象调用对象中的方法
var o = {a:"abc", b:{ c:function(param){ alert(this.a); //这里的this指向的不是o而是b,所以this是没有a属性的,这 ...
- 单元测试unit test,集成测试integration test和功能测试functional test的区别
以下内容转自 https://codeutopia.net/blog/2015/04/11/what-are-unit-testing-integration-testing-and-function ...
- C#委托(Delegate)学习日记
在.NET平台下,委托类型用来定义和响应应用程序中的回调.事实上,.NET委托类型是一个类型安全的对象,指向可以以后调用的其他方法.和传统的C++函数指针不同,.NET委托是内置支持多路广播和异步方法 ...
- bzoj 3751: [NOIP2014]解方程 同余系枚举
3.解方程(equation.cpp/c/pas)[问题描述]已知多项式方程:a ! + a ! x + a ! x ! + ⋯ + a ! x ! = 0求这个方程在[1, m]内的整数解(n 和 ...
- The type java.lang.String cannot be resolved. It is indirectly referenced from required .class files
最近在做J2ME开发项目,配置环境一切OK,但是打开项目时某些文件提示: The type java.lang.String cannot be resolved. It is indirectly ...
- httpclient 超时设置
最近项目客户反应超时经常出现:现已经总结超时设置: 使用是apache的HttpClient: DefaultHttpClient:请求超时httpclient.getParams().setPara ...
- android 判断程序是首次(第一次)进入
很多时候,我们需要判断用户是不是第一次进入程序,以决定是不是给用户一些操作提示. 这种功能的实现,说到底还是将数据(一个标志位)存储起来,下次进入程序的时候读取数据进行判断. 我这里只给出一种较简单的 ...
- lc面试准备:Implement Stack using Queues
1 题目 Implement the following operations of a stack using queues. push(x) -- Push element x onto stac ...
- PL/SQL游标使用
游标是用来处理使用SELECT语句从数据库中检索到的多行记录的工具.借助游标的功能,数据库应用程序可以对一组记录逐个进行处理,每次处理一行. 游标是从数据表中提取出来的数据,以临时表的形式存放在内存中 ...
- HDFS Block Replica Placement实现原理
1. 背景 Block Replica Placement——数据块复本存储策略,HDFS Namenode以此为依据选取数据块复本应存储至哪些HDFS Datanodes,策略的设计需要权衡以下 ...