您的位置首页>企业动态>

你了解Linux 块设备驱动?

导读 大家好,我是极客范的本期栏目编辑小友,现在为大家讲解你了解Linux 块设备驱动?问题。linux下的Ramdisk驱动程序1什么是内存磁盘?Ramdis

大家好,我是极客范的本期栏目编辑小友,现在为大家讲解你了解Linux 块设备驱动?问题。

linux下的Ramdisk驱动程序

1什么是内存磁盘?

Ramdisk是一种模拟磁盘,其数据实际上存储在RAM中。它使用一部分内存空间来模拟磁盘设备,并以块设备的形式组织和访问这块内存。对于用户来说,Ramdisk可以像普通硬盘分区一样使用。经常访问且不会改变的文件可以通过Ramdisk存储在内存中,可以明显提高系统的响应性能。

2内存磁盘的生成过程

近年来,计算机的CPU、内存、显卡等主要配件的性能得到了快速提升,相应的磁盘系统的性能日益成为整个计算机系统性能提升的瓶颈。虽然磁盘外部接口也从ATA33发展到如今的SATA 6Gbit/s。但是,这仍然不能完全解决磁盘瓶颈问题,尤其是在运行一些对数据访问速度要求较高的程序时,如数字图像处理或玩3D游戏加载纹理数据时,由于磁盘访问速度的影响,屏幕往往会出现延迟和暂停。于是,虚拟磁盘技术(Ramdisk)应运而生,它可以解决上述问题的“燃眉之急”。

随机磁盘3的特性

Ramdisk是一种基于内存的块设备,它使用内存作为实际的存储介质,但被组织为块设备,因此访问速度比实际磁盘快。但这也带来了另一个问题。当系统重启且内存断电时,存储在Ramdisk中的数据将消失。因此,Ramdisk不适合长期存储文件。[2]

4内存磁盘的作用

Ramdisk磁盘是保存加密数据的福音,因为如果我们将加密文件解密到普通磁盘,即使我们稍后删除解密文件,数据也会保留在磁盘上。这非常不安全。对于拉姆迪亚克来说,没有这样的问题。另外,假设有几个文件要经常使用。如果添加到内存中,程序的运行速度会大大提高,这是由存储介质的特性决定的(因为内存的读写速度远高于硬盘)。像网络服务器这样的计算机需要读取和交换大量的特定文件。因此,在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 ga

o_rd_init(void){ int i; int err = -ENOMEM; for(i=0; i < GAO_RD_MAX_DEVICE; i++) { vdisk[i] = vmalloc(GAO_RD_SIZE); } if(register_blkdev(GAO_RD_DEV_MAJOR, GAO_RD_DEV_NAME)) { err = -EIO;     goto out; } for(i = 0; i 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); 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; } 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); 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; } bio_endio(bio, 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 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); } if(register_blkdev(GAO_RD_DEV_MAJOR, GAO_RD_DEV_NAME))//对此块设备进行注册 { err = -EIO; goto out; } for(i = 0; i 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

 

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。