Linux 块设备驱动 (二)
linux下Ramdisk驱动
1 什么是Ramdisk
Ramdisk是一种模拟磁盘,其数据实际上是存储在RAM中,它使用一部分内存空间来模拟出一个磁盘设备,并以块设备的方式来组织和访问这片内存。对于用户来说可以把Ramdisk与通常的硬盘分区同等对待来使用。那些经常被访问、并且不会被更改的文件,可以通过Ramdisk被存放在内存中,这样能够明显地提高系统的响应性能。
2 Ramdisk的产生过程
近几年来,计算机的CPU、内存和显卡等主要配件的性能都提升得很快,而与之相对应的磁盘系统性能正越来越严重地成为整个电脑系统性能提升的瓶颈。虽然磁盘外部接口也从以前的ATA33发展到今天的SATA 6Gbit/s。但是,这还是不能彻底解决磁盘瓶颈的问题,特别是在运行一些对数据存取速度要求很高的程序,如数字影像处理或玩3D游戏装入纹理数据时,受磁盘存取速度的影响,屏幕画面时常会出现延迟和停顿。于是,虚拟磁盘技术(Ramdisk)应运而生,它可解上述问题的“燃眉之急”。
3 Ramdisk的特点
Ramdisk是基于内存的块设备,以内存作为实际的存储介质,但以块设备的方式组织,所以它具有比实际磁盘更快的存取速度。但这也带来了另一个问题,当系统重启,内存掉电后,Ramdisk中存储的数据也将会随之消失。所以Ramdisk不适合作为长期保存文件的介质。[2]
4 Ramdisk的作用
Ramdisk磁盘对于保存加密数据来说是一个福音,因为我们如果将加密的文件解密到普通的磁盘的话,即使我们随后删除了解密文件,数据仍然会留在磁盘上。这样是非常不安全的。而对于Ramdiak来说,就不存在这样的问题。另外,假设有几个文件要频繁的使用,你如果将它们加到内存当中,程序运行速度会大幅提高,这是由存储介质的特性决定的(因为内存的读写速度远高于硬盘)。像Web服务器这样的计算机,需要大量的读取和交换特定的文件,因此,在Web服务器上建立Ramdisk会大大提高网络读取的速度。
5 主要代码的解释
在程序的开始,首先定义了几个宏,用于Ramdisk驱动中用到的一些变量的统一赋值:
#define GAO_RD_DEV_NAME "gao_rd" //设备名称 #define GAO_RD_DEV_MAJOR 220 //主设备号 #define GAO_RD_MAX_DEVICE 2 //最大设备数 #define GAO_BLOCKSIZE 1024 //块大小 #define GAO_RD_SECTOR_SIZE 512 //扇区大小 #define GAO_RD_SIZE (4*1024*1024) //总大小 #define GAO_RD_SECTOR_TOTAL (GAO_RD_SIZE/GAO_RD_SECTOR_SIZE) //扇区数
这些宏变量描述了驱动程序的一些基本的属性和参数,是Ramdisk的基本信息。
本驱动程序主要完成了如下几个函数:
1) 驱动模块的初始化函数
int gao_rd_init(void);//初始化
此函数主要完成驱动模块的初始化和必要资源分配的工作。首先,为虚拟磁盘分配必要的内存空间,用作它的存储空间。然后用设备号和设备的名称将此块设备注册到内核中去。接下来要完成的任务就是分配通用磁盘结构体gendisk并赋上相应的值,分配请求队列以及帮定此设备的制造请求函数和请求队列。最后将通用磁盘结构体gendisk添加到内核中的相关队列里。
代码如下:
int gao_rd_init(void)
{
int i;
int err = -ENOMEM; for(i=; i < GAO_RD_MAX_DEVICE; i++)
{
vdisk[i] = vmalloc(GAO_RD_SIZE);
} /*注册vrd设备驱动程序*/
if(register_blkdev(GAO_RD_DEV_MAJOR, GAO_RD_DEV_NAME))/*对此块设备进行注册*/
{
err = -EIO;
goto out;
} for(i = ; i < GAO_RD_MAX_DEVICE; i++)
{
device[i].data = vdisk[i]; /*分配gendisk结构题,gendisk结构题是注册会设备的信息结构体*/
device[i].gd = alloc_disk();
if (!device[i].gd)
goto out; device[i].queue = blk_alloc_queue(GFP_KERNEL);// 分配正常的内核
if (!device[i].queue)
{
put_disk(device[i].gd); goto out;
}
blk_queue_make_request(device[i].queue, &gao_rd_make_request);
blk_queue_hardsect_size(device[i].queue,GAO_BLOCKSIZE);//盘块大小 device[i].gd->major = GAO_RD_DEV_MAJOR;
device[i].gd->first_minor = i;
device[i].gd->fops = &vrd_fops;//块设备操作结构体
device[i].gd->queue = device[i].queue;
device[i].gd->private_data = &device[i];
sprintf(device[i].gd->disk_name, "gao_rd%c" , 'a'+i);//
set_capacity(device[i].gd,GAO_RD_SECTOR_TOTAL);
add_disk(device[i].gd);
} printk("RAMDISK driver initialized!"); return ;
out:
while (i--) {
put_disk(device[i].gd);
blk_cleanup_queue(device[i].queue);
} return err;
}
2 驱动模块的卸载函数
void gao_rd_exit(void);//模块卸载函数
此函数完成与初始化函数相反的操作。它会删除此驱动模块被分配的通用磁盘结构体,利用设备号和设备名称删除对此设备的注册并释放此设备曾占用的存储空间。
代码如下:
void gao_rd_exit(void)
{
int i;
for(i = ; i < GAO_RD_MAX_DEVICE; i++)
{
del_gendisk(device[i].gd);//删除gendisk结构体
put_disk(device[i].gd);//减少gendisk结构体的引用计数
blk_cleanup_queue(device[i].queue);
}
unregister_blkdev(GAO_RD_DEV_MAJOR, GAO_RD_DEV_NAME);
for(i=;i < GAO_RD_MAX_DEVICE; i++)
{
vfree(vdisk[i]);
}
}
3 驱动模块的制造请求函数
static int gao_rd_make_request(struct request_queue *q, struct bio *bio);//制造请求函数
此函数处理设备使用过程中的实际I/O请求。
它会通过bio结构体获取此次I/O请求的相关信息,例如,存取标志、存取位置、请求队列等必要的I/O信息。之后遍历请求队列中的每一个段,如果是读数据请求,便将指定位置的数据拷贝到缓冲区中;如果是写数据请求,便将缓冲区的数据拷贝到指定的位置上。
代码如下:
static int gao_rd_make_request(struct request_queue *q, struct bio *bio)
{
gao_rd_device *pdevice;
char *pVHDDData;
char *pBuffer;
struct bio_vec *bvec;
int i;
if(((bio->bi_sector*GAO_RD_SECTOR_SIZE) + bio-> bi_size) > GAO_RD_SIZE)
{
bio_io_error(bio/*, bio->bi_size*/);
return ;
}
else
{
pdevice = (gao_rd_device *) bio->bi_bdev->bd_disk-> private_data;
pVHDDData = pdevice->data + (bio-> bi_sector*GAO_RD_SECTOR_SIZE);
bio_for_each_segment(bvec, bio, i)/*循环遍历每一个段*/
{
pBuffer = kmap(bvec->bv_page) + bvec-> bv_offset;
switch(bio_data_dir(bio))
{
case READA :
case READ : memcpy(pBuffer, pVHDDData, bvec-> bv_len);
break;
case WRITE : memcpy(pVHDDData, pBuffer, bvec-> bv_len);
break;
default : kunmap(bvec->bv_page);
bio_io_error(bio);
return ;
}
kunmap(bvec->bv_page);//取消内存页地址映射
pVHDDData += bvec->bv_len;
}
/*结束处理,并终止gao_rd_make_request函数*/
bio_endio(bio,);
return ;
}
}
4、编译驱动模块
要编译此驱动模块需要先编写一个Makefile文件:
KERNELDIR = /usr/src/kernels/2.6.27.10--i686/ #指定内核路径 PWD := $(shell pwd) CC =gcc #指定编译器为gcc obj-m := gao_rd.o modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules rm -rf *.o *.mod.c *.mod.o *.o *.order *.symvers #清除编译后的其它文件
之后在linux的shell终端下执行make命令进行编译:
[root@localhost ramdisk]# make
make -C /usr/src/kernels/2.6.27.10--i686/ M=/root/Desktop/work modules make[]: Entering directory `/usr/src/kernels/2.6.27.10--i686' CC [M] /root/Desktop/work/gao_rd.o Building modules, stage . MODPOST modules CC /root/Desktop/work/gao_rd.mod.o LD [M] /root/Desktop/work/gao_rd.ko make[]: Leaving directory `/usr/src/kernels/2.6.27.10--i686' rm -rf *.o *.mod.c *.mod.o *.o *.order *.symvers
现在,在目录下回有一个gao_rd.ko的文件,这个就是编译出来的可以加载的模块文件。
用insmod命令加载此模块后,在/proc/modules文件里就会看到此模块已被加载。
5、测试驱动模块
此驱动模块被加载到内核中后就可以利用此模块创建一个虚拟磁盘设备了。主要步骤如下:
1)#mkdir /root/Desktop/ramdisk/gao_rd
这个命令的作用是创建一个文件夹,我们用这个文件夹作为虚拟磁盘设备的挂载点。
2)#mknod /dev/gao_rd0 b 220 0
创建一个块设备,指定块设备的主设备号是220,次设备号自动分配。现在,在/dev目录下就会多出一个块设备,名为gao_rd0。
3)#mke2fs /dev/gao_rd0
用ext2格式对此设备进行格式化。
至此,就完成了一个虚拟磁盘设备的创建。
4)#mount /dev/gao_rd0 /root/Desktop/ramdisk/gao_rd
这个命令的作用是将刚刚创建的虚拟磁盘设备挂载到第一步创建的挂载点上。这样,这个虚拟磁盘就可以使用了。我们可以用ls命令来查看这块虚拟磁盘设备。
以下是完整的代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>//定义了一些常用的函数原型
#include <linux/fs.h>//
#include <linux/errno.h>//一些出错的常量符号的宏
#include <linux/types.h>//定义了一些基本的数据类型。所有类型均定义为适当的数字类型长度。
#include <linux/fcntl.h>//文件控制选项头文件,
#include <linux/vmalloc.h>
#include <linux/hdreg.h>//定义了一些对硬盘控制器进行编程的一些命令常量符号。
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <asm/uaccess.h> /*设备名称,段大小,设备大小等信息的定义*/
#define GAO_RD_DEV_NAME "gao_rd" //设备名称
#define GAO_RD_DEV_MAJOR 220 //主设备号
#define GAO_RD_MAX_DEVICE 2 //最大设备数
#define GAO_BLOCKSIZE 1024
#define GAO_RD_SECTOR_SIZE 512 //扇区大小
#define GAO_RD_SIZE (4*1024*1024) //总大小
#define GAO_RD_SECTOR_TOTAL (GAO_RD_SIZE/GAO_RD_SECTOR_SIZE) //总扇区数 typedef struct
{
unsigned char *data;
struct request_queue *queue;
struct gendisk *gd;
}gao_rd_device; static char *vdisk[GAO_RD_MAX_DEVICE]; static gao_rd_device device[GAO_RD_MAX_DEVICE]; static int gao_rd_make_request(struct request_queue *q, struct bio *bio)/*制造请求函数*/
{
gao_rd_device *pdevice;
char *pVHDDData;
char *pBuffer;
struct bio_vec *bvec;
int i; if(((bio->bi_sector*GAO_RD_SECTOR_SIZE) + bio-> bi_size) > GAO_RD_SIZE)
{
bio_io_error(bio/*, bio->bi_size*/);
return ;
}
else
{
pdevice = (gao_rd_device *) bio->bi_bdev->bd_disk-> private_data;
pVHDDData = pdevice->data + (bio-> bi_sector*GAO_RD_SECTOR_SIZE); bio_for_each_segment(bvec, bio, i)/*循环遍历的宏*/
{
pBuffer = kmap(bvec->bv_page) + bvec-> bv_offset;//kmap()函数??? switch(bio_data_dir(bio))//??????????????????????????????
{
case READA :
case READ : memcpy(pBuffer, pVHDDData, bvec-> bv_len);
break;
case WRITE : memcpy(pVHDDData, pBuffer, bvec-> bv_len);
break;
default : kunmap(bvec->bv_page);
bio_io_error(bio);
return ;
} kunmap(bvec->bv_page);
pVHDDData += bvec->bv_len;
}
/*结束处理,并终止gao_rd_make_request函数*/
bio_endio(bio, /*bio->bi_size, */);
return ;
}
} int gao_rd_open(struct inode *inode, struct file *filp)
{
return ;
} int gao_rd_release (struct inode *inode, struct file *filp)
{
return ;
} int gao_rd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,unsigned long arg)
{
//return -ENOTTY;
int error;
struct block_device *bdev = inode->i_bdev;
if(cmd!= BLKFLSBUF)
{
return -ENOTTY;//不适当的I/O控制操作(没有tty终端)
}
error = -EBUSY;//资源正忙
down(&bdev->bd_mount_sem);
if(bdev->bd_openers <= )
{
truncate_inode_pages(bdev->bd_inode->i_mapping,);
error = ;
}
up(&bdev->bd_mount_sem);
return error;
}
//block_device_operations 结构体是对块设备操作的集合
static struct block_device_operations vrd_fops =
{
.owner = THIS_MODULE,
.open = gao_rd_open,
.release = gao_rd_release,
.ioctl = gao_rd_ioctl,
};
int gao_rd_init(void)
{
int i;
int err = -ENOMEM;
for(i=; i < GAO_RD_MAX_DEVICE; i++)
{
vdisk[i] = vmalloc(GAO_RD_SIZE);
}
/*注册vrd设备驱动程序*/
if(register_blkdev(GAO_RD_DEV_MAJOR, GAO_RD_DEV_NAME))//对此块设备进行注册
{
err = -EIO;
goto out;
}
/**/
for(i = ; i < GAO_RD_MAX_DEVICE; i++)
{
device[i].data = vdisk[i];
/*分配gendisk结构题,gendisk结构题是注册会设备的信息结构体*/
device[i].gd = alloc_disk(); if (!device[i].gd)
goto out; device[i].queue = blk_alloc_queue(GFP_KERNEL);//GFP_KERNEL 分配正常的内核
if (!device[i].queue)
{
put_disk(device[i].gd);
goto out;
} blk_queue_make_request(device[i].queue, &gao_rd_make_request);
blk_queue_hardsect_size(device[i].queue,GAO_BLOCKSIZE);//盘块大小 device[i].gd->major = GAO_RD_DEV_MAJOR;
device[i].gd->first_minor = i;
device[i].gd->fops = &vrd_fops;//块设备操作结构体
device[i].gd->queue = device[i].queue;
device[i].gd->private_data = &device[i];
sprintf(device[i].gd->disk_name, "gao_rd%c" , 'a'+i);//
set_capacity(device[i].gd,GAO_RD_SECTOR_TOTAL); add_disk(device[i].gd);
}
printk("RAMDISK driver initialized!");
return ;
out:
while (i--) {
put_disk(device[i].gd);
blk_cleanup_queue(device[i].queue);
}
return err;
} void gao_rd_exit(void)
{
int i;
for(i = ; i < GAO_RD_MAX_DEVICE; i++)
{
del_gendisk(device[i].gd);//删除gendisk结构体
put_disk(device[i].gd);//减少gendisk结构体的引用计数
blk_cleanup_queue(device[i].queue);
}
unregister_blkdev(GAO_RD_DEV_MAJOR, GAO_RD_DEV_NAME);
for(i=;i < GAO_RD_MAX_DEVICE; i++)
{
vfree(vdisk[i]);
}
}
module_init(gao_rd_init);
module_exit(gao_rd_exit); MODULE_LICENSE("Dual BSD/GPL");
KERNELDIR = /usr/src/kernels/2.6.27.10--i686/ PWD := $(shell pwd) CC =gcc
obj-m := gao_rd.o
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules rm -rf *.o *.mod.c *.mod.o *.o *.order *.symvers
Linux 块设备驱动 (二)的更多相关文章
- Linux块设备驱动(二) _MTD驱动及其用户空间编程
MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...
- linux块设备驱动之实例
1.注册:向内核注册个块设备驱动,其实就是用主设备号告诉内核这个代表块设备驱动 sbull_major = register_blkdev(sbull_major, "sbull&quo ...
- Linux块设备驱动详解
<机械硬盘> a:磁盘结构 -----传统的机械硬盘一般为3.5英寸硬盘,并由多个圆形蝶片组成,每个蝶片拥有独立的机械臂和磁头,每个堞片的圆形平面被划分了不同的同心圆,每一个同心圆称为一个 ...
- linux 块设备驱动 (三)块设备驱动开发
一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...
- linux块设备驱动(一)——块设备概念介绍
本文来源于: 1. http://blog.csdn.net/jianchi88/article/details/7212370 2. http://blog.chinaunix.net/uid-27 ...
- Linux 块设备驱动 (一)
1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ...
- Linux块设备驱动(一) _驱动模型
块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ...
- linux块设备驱动
块设备驱动程序<1>.块设备和字符设备的区别1.读取数据的单元不同,块设备读写数据的基本单元是块,字符设备的基本单元是字节.2.块设备可以随机访问,字符设备只能顺序访问. 块设备的访问:当 ...
- Linux块设备驱动_WDS
推荐书:<Linux内核源代码情景分析> 1.字符设备驱动和使用中等待某一事件的方法①查询方式②休眠唤醒,但是这种没有超时时间③poll机制,在休眠唤醒基础上加一个超时时间④异步通知,异步 ...
- linux块设备驱动---程序设计(转)
块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigned i ...
随机推荐
- java_接口的应用
package com.test; interface USB{ //创建一个USB接口,所有的操作要按照这个标准来工作 void start();//默认为public void stop(); } ...
- web项目跨域访问
1.同域相互访问 假设A.html 与 b.html domain都是localhost (同域) A.html中iframe 嵌入 B.html,name=myframe A.html有js fun ...
- [RxJS] Updating Data with Scan
You often need to update the data flowing through the stream with custom logic based on what you nee ...
- jquery获取当前元素坐标
1. jquery获取当前元素坐标 A) 获取对象
- ubuntu16.04 server安装小记
由于本人有一台闲置的thinkpad电脑,所以打算在上边安装一个ubuntu16.04 server版本,其中遇到主要问题,做一下记录: 安装过程中出现“ubuntu16.04 server64 bu ...
- javascript将毫秒还原为可读时间格式
<script type="text/javascript"> //随便设置一个时间 var otime = new Date("2015-11-11 20: ...
- 0130——UIScrollView
1.contentSize幕布实际大小决定滚动的方向,如果小于图片本身不滚动,默认也是不滚动 view.contentSize = CGSizeMake(1280, 200); 而frame只是用来显 ...
- C# 参数按照ASCII码从小到大排序(字典序)
在对接第三方支付的时候,第三方会要求参数按照ASCII码从小到大排序.如下: public static void requestPay() { Dictionary<string, strin ...
- Lucene文件扩展名
名称 文件后缀 描述 段文件(Segments File) segments.gen segments_N 存储提交点信息 锁文件(Lock File) write.lock 用来阻止多个indexW ...
- C#this的五种用法
this的五种用法: 1.使用被掩盖的成员变量: class AA { int a; public void set1(int a) { this.a = a;//right } public voi ...