vmem驱动设备
vmem是内存多字符设备。包含vfs的open、read、write、ioctl、poll、fasync和release函数,device文件的读写。
virtual_mem.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/poll.h> //#undef DEBUG
#define DEBUG
#ifdef DEBUG
#define dprintk(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#else
#define dprintk(...) do { } while (0)
#endif static struct class *virtualmem_class = NULL; static int virtualmem_major = ; //模块virtualmem_major参数,默认为0
static int virtualmem_minor = ;
module_param(virtualmem_major, int, S_IRUGO); static int dev_num = ; //模块dev_num参数,默认为1
module_param(dev_num, int, S_IRUGO); struct virtualmem_dev {
unsigned int current_len;
unsigned char mem[PAGE_SIZE];
struct cdev cdev;
struct mutex mutex;
struct device_attribute device_attribute;
wait_queue_head_t r_wait;
wait_queue_head_t w_wait;
struct fasync_struct *async_queue; };
static struct virtualmem_dev *virtualmem_devp; ssize_t virtual_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct virtualmem_dev *devp = (struct virtualmem_dev *)dev_get_drvdata(dev);
mutex_lock(&devp->mutex);
snprintf(buf, devp->current_len + , "%s", devp->mem);
mutex_unlock(&devp->mutex);
return devp->current_len + ;
} ssize_t virtual_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct virtualmem_dev *devp = (struct virtualmem_dev *)dev_get_drvdata(dev);
mutex_lock(&devp->mutex);
if (PAGE_SIZE == devp->current_len) {
devp->current_len = ;
return -;
}
memcpy(devp->mem, buf, count);
devp->current_len = count;
mutex_unlock(&devp->mutex);
return count;
} static int virtual_open(struct inode *inode, struct file *filp)
{
struct virtualmem_dev *devp;
devp = container_of(inode->i_cdev, struct virtualmem_dev, cdev);
filp->private_data = devp;
return ;
} static ssize_t virual_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int ret;
struct virtualmem_dev *devp = filp->private_data;
DECLARE_WAITQUEUE(wait, current); mutex_lock(&devp->mutex);
add_wait_queue(&devp->r_wait, &wait); //定义读队列唤醒 while (!devp->current_len) {
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
goto out1;
} set_current_state(TASK_INTERRUPTIBLE); //读阻塞休眠
mutex_unlock(&devp->mutex);
schedule();
if (signal_pending(current)) {
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&devp->mutex);
} if (count > devp->current_len)
count = devp->current_len;
if (copy_to_user(buf, devp->mem, count)) {
ret = -EFAULT;
goto out1;
} else {
devp->current_len -= count;
memcpy(devp->mem, devp->mem + count, devp->current_len);
dprintk("read %d bytes,current_len:%d\n", (int)count, devp->current_len);
wake_up_interruptible(&devp->w_wait); //唤醒读阻塞休眠
ret = count;
}
out1:
mutex_unlock(&devp->mutex);
out2:
remove_wait_queue(&devp->r_wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
} static ssize_t virual_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
int ret;
struct virtualmem_dev *devp = filp->private_data;
DECLARE_WAITQUEUE(wait, current); mutex_lock(&devp->mutex);
add_wait_queue(&devp->w_wait, &wait); //定义写队列唤醒 while (devp->current_len == PAGE_SIZE) {
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
goto out1;
} set_current_state(TASK_INTERRUPTIBLE); //写阻塞休眠
mutex_unlock(&devp->mutex);
schedule();
if (signal_pending(current)) {
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&devp->mutex);
}
if (count > PAGE_SIZE - devp->current_len)
count = PAGE_SIZE - devp->current_len;
if (copy_from_user(devp->mem + devp->current_len, buf, count)) {
ret = -EFAULT;
goto out1;
} else {
devp->current_len += count;
dprintk("written %d bytes,current_len:%d\n", (int)count, devp->current_len);
wake_up_interruptible(&devp->r_wait); //唤醒读阻塞休眠 if (devp->async_queue) { //写信号进行异步通知应用
kill_fasync(&devp->async_queue, SIGIO, POLL_IN);
dprintk("%s kill SIGIO\n", __func__);
}
ret = count;
}
out1:
mutex_unlock(&devp->mutex);
out2:
remove_wait_queue(&devp->w_wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
} //这里利用IO CMD宏定义
#define MEM_CLEAR _IO('V',1)
#define MEM_FULL _IOW('V', 2, unsigned char) static long virtual_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct virtualmem_dev *devp = filp->private_data;
switch(cmd) {
case MEM_CLEAR:
mutex_lock(&devp->mutex);
devp->current_len = ;
memset(devp->mem, , PAGE_SIZE);
mutex_unlock(&devp->mutex);
dprintk("cmd = %d, clear the memory", cmd);
break;
case MEM_FULL:
mutex_lock(&devp->mutex);
devp->current_len = PAGE_SIZE;
memset(devp->mem, (unsigned char )arg, PAGE_SIZE);
mutex_unlock(&devp->mutex);
dprintk("cmd = %d, fill the memory using %ld", cmd, arg);
break;
default:
return -EINVAL;
}
return ;
} static unsigned int virtual_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = ;
struct virtualmem_dev *devp = filp->private_data;
mutex_lock(&devp->mutex);
poll_wait(filp, &devp->r_wait, wait); //声明读写队列到poll table唤醒线程
poll_wait(filp, &devp->w_wait, wait); if (devp->current_len != )
mask |= POLLIN | POLLRDNORM; if (devp->current_len != PAGE_SIZE)
mask |= POLLOUT | POLLWRNORM; mutex_unlock(&devp->mutex);
return mask;
} static int virtual_fasync(int fd, struct file *filp, int mode)
{
struct virtualmem_dev *devp = filp->private_data;
return fasync_helper(fd, filp, mode, &devp->async_queue);
} static int virtual_release(struct inode *inode, struct file *filp)
{
virtual_fasync(-, filp, );
return ;
} static struct file_operations virtual_fops = {
.owner = THIS_MODULE,
.read = virual_read,
.write = virual_write,
.unlocked_ioctl = virtual_ioctl,
.poll = virtual_poll,
.fasync = virtual_fasync,
.open = virtual_open,
.release = virtual_release,
}; static int virtualmem_setup_dev(struct virtualmem_dev *devp, int index)
{
int ret;
dev_t devno = MKDEV(virtualmem_major, virtualmem_minor + index); cdev_init(&devp->cdev, &virtual_fops);
devp->cdev.owner = THIS_MODULE;
ret = cdev_add(&devp->cdev, devno, );
return ret;
} static int __init virtualmem_init(void)
{
int ret;
int i;
dev_t devno = MKDEV(virtualmem_major, virtualmem_minor);
struct device *temp[dev_num]; dprintk("Initializing virtualmem device.\n");
if(virtualmem_major)
ret = register_chrdev_region(devno, dev_num, "virtualmem");
else {
ret = alloc_chrdev_region(&devno, , dev_num, "virtualmem");
virtualmem_major = MAJOR(devno);
virtualmem_minor = MINOR(devno);
}
if(ret < ) {
dprintk("Failed to alloc char dev region.\n");
goto err;
} virtualmem_devp = kzalloc(sizeof(struct virtualmem_dev) * dev_num, GFP_KERNEL);
if (!virtualmem_devp) {
ret = -ENOMEM;
dprintk("Failed to alloc virtualmem device.\n");
goto unregister;
} for(i = ; i < dev_num; i++) {
ret = virtualmem_setup_dev(virtualmem_devp + i, i);
if (ret) {
dprintk("Failed to setup dev %d.\n", i);
while(--i >= )
cdev_del(&(virtualmem_devp + i)->cdev);
goto kfree;
}
} virtualmem_class = class_create(THIS_MODULE, "virtualmem"); //建立virtualmem类
if (IS_ERR(virtualmem_class)) {
ret = PTR_ERR(virtualmem_class);
dprintk("Failed to create virtualmem class.\n");
goto destroy_cdev;
} for(i = ; i < dev_num; i++) {
//在virtualmem这个类里建立多个virtualmem文件
temp[i] = device_create(virtualmem_class, NULL, devno + i, (void *)(virtualmem_devp + i), "%s%d", "virtualmem", i);
if (IS_ERR(temp[i])) {
ret = PTR_ERR(temp[i]);
dprintk("Failed to create virtualmem device.\n");
while(--i >= )
device_destroy(virtualmem_class, devno + i);
goto destory_class;
}
}
for(i = ; i < dev_num; i++) {
(virtualmem_devp + i)->device_attribute.attr.name = "mem"; //对于单设备一般用宏 DEVICE_ATTR,这里多设备需要完成宏的代码
(virtualmem_devp + i)->device_attribute.attr.mode = S_IRUGO | S_IWUSR;
(virtualmem_devp + i)->device_attribute.show = virtual_show;
(virtualmem_devp + i)->device_attribute.store = virtual_store;
ret = device_create_file(temp[i], &(virtualmem_devp + i)->device_attribute);
if(ret < ) {
dprintk("Failed to create attribute mem.");
while(--i >= )
device_remove_file(temp[i], &(virtualmem_devp + i)->device_attribute);
goto destroy_device;
}
}
for(i = ; i < dev_num; i++) {
mutex_init(&(virtualmem_devp + i)->mutex); //初始化互斥锁
init_waitqueue_head(&(virtualmem_devp + i)->r_wait); //初始化读写队列
init_waitqueue_head(&(virtualmem_devp + i)->w_wait);
} dprintk("Succedded to Initialize virtualmem device.\n");
return ; destroy_device:
for(i = ; i < dev_num; i++)
device_destroy(virtualmem_class, devno + i); destory_class:
class_destroy(virtualmem_class); destroy_cdev:
for(i = ; i < dev_num; i++)
cdev_del(&(virtualmem_devp + i)->cdev); kfree:
kfree(virtualmem_devp); unregister:
unregister_chrdev_region(MKDEV(virtualmem_major, virtualmem_minor), dev_num); err:
return ret;
} static void __exit virualmem_exit(void)
{
int i;
dprintk("Destroy virtualmem device.\n");
if(virtualmem_class) {
for(i = ; i < dev_num; i++)
device_destroy(virtualmem_class, MKDEV(virtualmem_major, virtualmem_minor + i));
class_destroy(virtualmem_class);
}
if(virtualmem_devp) {
for(i = ; i < dev_num; i++)
cdev_del(&(virtualmem_devp + i)->cdev);
kfree(virtualmem_devp);
}
unregister_chrdev_region(MKDEV(virtualmem_major, virtualmem_minor), dev_num);
} module_init(virtualmem_init);
module_exit(virualmem_exit); MODULE_AUTHOR("Kevin Hwang <kevin.hwang@live.com");
MODULE_LICENSE("GPL v2");
测试程序virtual_test.c:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/epoll.h>
#include <sys/stat.h> //#undef EPOLL
#define EPOLL
#define MEM_CLEAR _IO('V',1)
#define MEM_FULL _IOW('V', 2, unsigned char) char buff[];
int fd;
void signalio_handler(int signum)
{
printf("receive a signal \n");
read(fd, buff, );
printf("%s \n", buff);
} int main(int argc, char const **argv)
{
int oflags, input_num = , dev_num;
char tmp[];
char path[]; #ifdef EPOLL
int ret;
struct epoll_event ev_virtualmem;
int epfd;
#endif fd = open("/sys/module/virtual_mem/parameters/dev_num", O_RDONLY, S_IRUSR); //读取模块设备数量参数dev_num
if (fd == -) {
printf("Failed to open parameters for dev_num.\n");
return -;
}
read(fd, tmp, );
dev_num = atoi(tmp); //获取dev_num
if (dev_num > ) { //如果dev_num = 1默认不需要参数
if (argc == )
input_num = atoi(*(++argv));
else {
printf("please input the dev_num between 0 and %d.\n", dev_num - );
return -;
}
if (**argv < '' || **argv > '' || dev_num <= input_num) { //第一个参数第一位不为0~9返回错误信息
printf("please input the dev_num between 0 and %d.\n", dev_num - );
return -;
}
} snprintf(path, + , "/dev/virtualmem%d", input_num); //获取设备路径名字 fd = open(path, O_RDWR | O_NONBLOCK, S_IRUSR | S_IWUSR);
if (fd == -) {
printf("Failed to open /dev/virtualmem%d.\n", input_num);
return -;
}
printf("open /dev/virtualmem%d success.\n", input_num); if (ioctl(fd, MEM_FULL, 0xFF) < )
printf("ioctl command = 0x%lx failed.\n", MEM_FULL); if (ioctl(fd, MEM_CLEAR, 0xFF) < )
printf("ioctl command = 0x%x failed.\n", MEM_CLEAR);
#ifdef EPOLL
epfd = epoll_create(); //创建epoll
if (epfd < ) {
printf("epoll_create failed.\n");
return -;
}
ev_virtualmem.events = EPOLLIN | EPOLLPRI; //epoll触发事件
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev_virtualmem) < ) { //增加这个事件
printf("epfd_ctl add failed.\n");
return -;
} ret = epoll_wait(epfd, &ev_virtualmem, , ); //等待事件,调用驱动poll
if (ret < )
printf("epoll_wait failed.\n");
else if (!ret)
printf("no data input in virtualmem%d\n", input_num);
else {
printf("receive data.\n");
read(fd, buff, );
printf("%s \n", buff);
}
if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev_virtualmem)) {
printf("epfd_ctl delete failed.\n");
return -;
}
#else
signal(SIGIO, signalio_handler); //声明signalio_handler信号处理函数
fcntl(fd, F_SETOWN, getpid()); //获取程序pid
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC); //添加FASYNC标志
while ()
sleep();
#endif
close(fd);
return ;
}
Makefile:
obj-m += virtual_mem.o KERN_DIR = /lib/modules/$(shell uname -r)/build
all:
make -C $(KERN_DIR) M=$(CURDIR) modules clean:
make -C $(KERN_DIR) M=$(CURDIR) clean
github代码地址 https://github.com/kevinhwang91/virtual_mem
代码在linux3.0-4.2测试过,使用方法:
make
insmod virtual_mem.ko (dev_num=具体数字,dev_num缺省默认为1)
可以直接在/dev/virtual_mem?或者/sys/class/virtualmem/virtualmem?/mem(非阻塞)进行读写
gcc -o virtual_test virtual_test.c(可以在源码预处理选择处理方法)
./virtual_test (0 - dev_num-1)其中一个设备
vmem驱动设备的更多相关文章
- linux平台总线驱动设备模型之点亮LED
这一节里,我们来使用平台驱动设备这一套架构来实现我们之前使用简单的字符设备驱动点亮LED,这里并无实际意义,只是告诉大家如果编写平台总线驱动设备. 问:如何编写平台总线驱动设备这一套架构的设备驱动? ...
- 驱动程序分层分离概念_总线驱动设备模型_P
分层概念: 驱动程序向上注册的原理: 比如:输入子程序一个input.c作为一层,下层为Dev.c和Dir.c,分别编写Dev.c和Dir.c向上Input.c注册:如图所示 分离概念: 分离概念主要 ...
- Android集成C程序访问驱动设备节点
1.前言 Android系统中,应用程序一般是使用Java语言进行开发的,但是通过C语言也可以进行Android中的可执行程序开发,接下来,将简单介绍在Android系统中如何通过C程序来访问内核中s ...
- 旧接口注册LED字符驱动设备(静态映射)
#include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module ...
- 旧接口注册LED字符驱动设备(动态映射)
#include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module ...
- linux查看硬件信息及驱动设备相关整理
查看声卡设备:cat /proc/asound/cards 查看USB设备:cat /proc/bus/usb/devices 常用命令整理如下:用硬件检测程序kuduz探测新硬件:service k ...
- Linux 网卡驱动设备程序设计(1)
一.网卡驱动架构分析 1. Linux 网络子系统 #系统调用接口层 为应用程序提供访问网络子系统的统一方法. #协议无关层 提供通用的方法来使用传输层协议. #协议栈的实现 实现具体的网络协议 #设 ...
- Linux驱动设备中的并发控制
一.基本概念 二.中断屏蔽 三.原子操作 四.自旋锁 五.信号量 六.互斥体 七.自旋锁与信号量的比较 Linux设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发的访问会导致竞态,即使 ...
- Linux平台总线驱动设备模型
platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver.Linux 2.6的设备驱动模型中,把I2C.RTC.LCD等都归纳为pl ...
随机推荐
- iOS 视频全屏功能 学习
项目中,也写过类似"视频全屏"的功能, 前一阵子读到今日头条 的一篇技术文章,详细介绍三种旋转方法差异优劣最终择取.文章从技术角度看写的非常好,从用户角度看,也用过多家有视频功能的 ...
- $Android设置TextView的字体
做项目的时候,需要使用到手写字体来让内容更加的美观.可是程序中默认使用的是系统的默认字体,怎么将TextView(或EditText)的字体设置成自己想要的字体呢?步骤如下: 1.下载字体文件(.tt ...
- flex 实现图片播放 方案二 把临时3张图片预加载放入内存
该方案,是预加载:前一张,当前,下一张图片,一共3张图片放入内存中.这样对内存的消耗可以非常小,加载之后的图片就释放内存. 下面示例一个是类ImagePlayers,一个是index.mxml pac ...
- MYSQL数据库学习笔记1
MYSQL数据库学习笔记1 数据库概念 关系数据库 常见数据库软件 SQL SQL的概念 SQL语言分类 数据库操作 创建数据库 查看数据库的定义 删除数据库 修改数据库 创建表 数据类型 约束 ...
- ETL应用:使用shell实现文件级校验的方法
BI应用中,对接口规范性约束很重要,接口文件提供需要配套提供该文件的校验文件,校验文件格式如下: 序号 信息内容 数据类型及长度 说明 1 接口数据文件名称 CHAR(50) 2 文件的大小(字节数) ...
- Windows下MarialDB使用
命令行控制启动和关闭:mysqld --console #这样启动ctrl+c即为关闭 启动:双击mysqld.exe即可 #此为后台启动 关闭:mysqladmin -uroot -pr ...
- 封装一个既能遍历数组又能遍历对象的的forEach函数
function newforEach(obj,fn) { var key; if(obj instanceof Array){ obj.forEach(function(item,index){ f ...
- mongodb $where 查询中的坑
mongodb 查询中坑就是数字开头的字段不能用点号,只能用[""].例如: 即:db.datas.find({$where:"this['54bcfc6c329af61 ...
- centos 源码安装php5.5
系统环境: CentOS 6.5 / 7.0 x86_64 Fedora 20 x86_64下载 PHP 源码包 # wget http://cn2.php.net/distributions/php ...
- mysql在表的某一位置增加一列的命令
如果想在一个已经建好的表中添加一列,可以用诸如: alter table t1 add column addr varchar(20) not null; 这条语句会向已有的表t1中加入一列addr, ...