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 ...
随机推荐
- WPF后台访问XAML元素
当我们需要从后台访问xaml文件时,我们可以通过这样的方式来操作: private void button1_Click(object sender, RoutedEventArgs e) { Sys ...
- 《C和指针》章节后编程练习解答参考——第5章
5.1 题目: 略 解答代码: #include <stdio.h> int main(void) { char ch; while (((ch = getchar()) != EOF) ...
- Junit4 架构设计系列(1): Request,ClassRequest 和 RunnerBuilder
Overall Junit的成功已不言而喻,其广泛应用于单元测试,测试驱动开发领域.大量的工具,IDE都集成了JUnit,著名的有Maven,Ant,Eclipse,甚至像Google SDK提供的A ...
- [译]36 Days of Web Testing(四)
Day 19: UX 用户体验 Why ? 最近UX变得越来越火,用户提现往往会直接联想到易用性和设计. 在我看来,UX不仅仅是这两点.UX, User Experience ,对我而言,不单单是产 ...
- bower的权限问题
装bootstrap的时候,先用sudo指令装了bower,但是一打 bower isntall bootstrap 就报错: Error: EACCES, permission denied '/U ...
- 调优UWSGI,后台启动,热更改PY,杜绝502
记得加启动参数: --daemonize /var/log/uwsgi.log --post-buffering 32768 --buffer-size 32768 reload.set #!/bin ...
- 用SharedPreferences保存List(Map(String, String))数据
原因: SharedPreferences没有保存数组的方法,但是有时候为了保存一个数组而进行序列化,或者动用sqlite都是有点杀猪焉用牛刀的感觉,所以就自己动手改进一下吧. 解决方案: 采用的方式 ...
- 【HDOJ】1429 胜利大逃亡(续)
BFS+状态压缩,做了很多状态压缩了.今晚把八数码问题给搞定了. #include <iostream> #include <queue> #include <cstri ...
- UC全屏
UC浏览器U3内核扩展接口定义
- Linux下如何挂载FAT32格式USB设备
挂u盘之前,运行命令cat /proc/partitions,看看现在系统中有哪些分区.插上u盘以后,再次运行上述命令,看看多出来什么分区.通常是sda1. 1.插入U盘 2.输入 fdisk -l ...