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=0; 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 = 0; i < GAO_RD_MAX_DEVICE; i++) { device[i].data = vdisk[i]; /*分配gendisk结构题,gendisk结构题是注册会设备的信息结构体*/ device[i].gd = alloc_disk(1);     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 0;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 = 0; 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=0;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 0; } 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 0; } kunmap(bvec->bv_page);//取消内存页地址映射 pVHDDData += bvec->bv_len; } /*结束处理,并终止gao_rd_make_request函数*/ bio_endio(bio,0); return 0; } }

4、编译驱动模块

要编译此驱动模块需要先编写一个Makefile文件:

KERNELDIR = /usr/src/kernels/2.6.27.10-1-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-1-i686/ M=/root/Desktop/work modulesmake[1]: Entering directory `/usr/src/kernels/2.6.27.10-1-i686' CC [M] /root/Desktop/work/gao_rd.o Building modules, stage 2. MODPOST 1 modules CC /root/Desktop/work/gao_rd.mod.o LD [M] /root/Desktop/work/gao_rd.komake[1]: Leaving directory `/usr/src/kernels/2.6.27.10-1-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 #include #include //定义了一些常用的函数原型#include //#include //一些出错的常量符号的宏#include //定义了一些基本的数据类型。所有类型均定义为适当的数字类型长度。#include //文件控制选项头文件,#include #include //定义了一些对硬盘控制器进行编程的一些命令常量符号。#include #include #include /*设备名称,段大小,设备大小等信息的定义*/#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 0; } 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 0; } kunmap(bvec->bv_page); pVHDDData += bvec->bv_len; } /*结束处理,并终止gao_rd_make_request函数*/ bio_endio(bio, /*bio->bi_size, */0); return 0; } }int gao_rd_open(struct inode *inode, struct file *filp){ return 0;}int gao_rd_release (struct inode *inode, struct file *filp){ return 0;}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 <= 2) { truncate_inode_pages(bdev->bd_inode->i_mapping,0); error = 0; } 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=0; 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 = 0; i < GAO_RD_MAX_DEVICE; i++) { device[i].data = vdisk[i]; /*分配gendisk结构题,gendisk结构题是注册会设备的信息结构体*/ device[i].gd = alloc_disk(1); 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 0;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 = 0; 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=0;i < GAO_RD_MAX_DEVICE; i++) { vfree(vdisk[i]); }}module_init(gao_rd_init);module_exit(gao_rd_exit);MODULE_LICENSE("Dual BSD/GPL");

Makefile代码

KERNELDIR = /usr/src/kernels/2.6.27.10-1-i686/PWD := $(shell pwd)CC =gccobj-m := gao_rd.o modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules rm -rf *.o *.mod.c *.mod.o *.o *.order *.symvers

linux 内核块设备驱动,你了解Linux 块设备驱动?相关推荐

  1. Linux内核学习之路_1_编译Linux内核

    1.准备工作 1.1 学习环境 1.2 下载Linux内核源码 1.3 解压Linux内核 1.4 目录结构介绍 1.2.2 Linux内核配置 1.1 学习环境 本系列教程使用的环境如下: 操作系统 ...

  2. linux内核分成如下五个子系统,linux内核主要由5个子系统 Linux内核由哪几个子系统组成?...

    1, Linux内核由哪几个子系统组成? Linux内核主要由五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信.1.进程调度(SCHED):控制进程对CPU的访问.当需要选择下一 ...

  3. 图解linux内核设计艺术,Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理...

    Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理 作者:新设计团队 出版日期:2011年05月 文件大小:29.01M 支持设备: ¥6.00在线试读 适用客户端: 言商书局 iPa ...

  4. 【Linux 内核】Linux 操作系统结构 ( Linux 内核在操作系统中的层级 | Linux 内核子系统及关系 | 进程调度 | 内存管理 | 虚拟文件系统 | 网络管理 | 进程间通信 )

    文章目录 一.Linux 内核在操作系统中的层级 二.Linux 内核子系统 三.Linux 内核子系统之间的关系 一.Linux 内核在操作系统中的层级 Linux 内核 所在层级 : 整个计算机系 ...

  5. Linux内核分析(三)----初识linux内存管理子系统

    原文:Linux内核分析(三)----初识linux内存管理子系统 Linux内核分析(三) 昨天我们对内核模块进行了简单的分析,今天为了让我们今后的分析没有太多障碍,我们今天先简单的分析一下linu ...

  6. linux内核锁死怎么解决_解决Linux内核中的2038年问题

    linux内核锁死怎么解决 由于时间在Linux中的表示方式,带符号的32位数字无法支持20:38(UTC)3:14:07之后的时间. 2038年 (Y2038或Y2K38)问题是关于时间数据类型表示 ...

  7. iso linux 内核版本号_Linux发行版和Linux内核的区别

    Linux内核版本与linux发行版本的区别 LINUX内核版本 是指系统内核的版本号.LINUX的内核具有两种不同的版本号,实验版本和产品化版本.要确定 LINUX版本 的类型,只要查看一下版本号: ...

  8. Linux内核学习(六):linux kernel的Kconfig分析

    Linux内核学习(六):linux kernel的Kconfig分析 前面我们知道了makefile文件,makefile文件会结合配置文件.config来进行操作.这里就再来看看生成内核.conf ...

  9. Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理

    为什么80%的码农都做不了架构师?>>>    Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理 本书使用高分辨率精心绘制的300多张图片,帮助大家理解操作系统特有 ...

  10. 【技术分享篇】Linux内核——手把手带你实现一个Linux内核文件系统丨Linux内核源码分析

    手把手带你实现一个Linux内核文件系统 1. 内核文件系统架构分析 2. 行行珠玑,代码实现 [技术分享篇]Linux内核--手把手带你实现一个Linux内核文件系统丨Linux内核源码分析 更多L ...

最新文章

  1. ASP调用存储过程返回了一个参数和一个记录集时出现ADODB.Recordset 错误 '800a0e78'...
  2. 《预训练周刊》第34期:使用图像级监督检测两万个类别、BigScience寻求建立强大的开放语言模型...
  3. Linux内存管理之一 分段与分页
  4. 关于iPhone 中sqlite文件的使用
  5. echarts 弹出放大_Echarts图标增加全屏/放大功能
  6. smokeping自动检测系统
  7. switch 选择语句
  8. nginx学习之详细安装篇(二)
  9. java cookie过期_在Express中,会话Cookie过期
  10. linux查找符合条件的文件并删除
  11. 介绍一个小技巧,如何实现ssh免密登录
  12. HDU1829 A Bug's Life 并查集
  13. SQL SERVER插件之SQLPrompt 激活使用
  14. Visual Studio助手VAssistx各版本破解教程
  15. 一文带你掌握抓包工具的使用-科来
  16. Unity使用Aspose.Words创建表格和UI截图一起插入到Word中并保存到本地的一种解决方案
  17. 10款优秀的在线格式转化器
  18. 洛谷P3376 【模板】网络最大流{Dinic算法}
  19. spark scala求PV,UV,topN
  20. 达梦数据库在linux下的安装步骤

热门文章

  1. HA应用之 -- corosync+pacemaker
  2. 路由交换机管理密码篇
  3. 解决UnicodeDecodeError: 'ascii' codec can't decod...
  4. 关于Remoting(续)
  5. 提示cannot instantiate abstract class due to following members?
  6. 通过CrawlSpider对招聘网站进行整站爬取(拉勾网实战)
  7. 第三阶段 15_JavaWeb基础_JQuery的事件处理
  8. javascript优化--01高质量编码
  9. iOS之NSURLConnection详解(2)
  10. regsvr32.exe进程注册dll文件