1 什么是Ramdisk
Ramdisk是一種模擬磁盤,其數(shù)據(jù)實(shí)際上是存儲在RAM中,它使用一部分內(nèi)存空間來模擬出一個磁盤設(shè)備,并以塊設(shè)備的方式來組織和訪問這片內(nèi)存。對于用戶來說可以把Ramdisk與通常的硬盤分區(qū)同等對待來使用。那些經(jīng)常被訪問、并且不會被更改的文件,可以通過Ramdisk被存放在內(nèi)存中,這樣能夠明顯地提高系統(tǒng)的響應(yīng)性能。
2 ?Ramdisk的產(chǎn)生過程
近幾年來,計(jì)算機(jī)的CPU、內(nèi)存和顯卡等主要配件的性能都提升得很快,而與之相對應(yīng)的磁盤系統(tǒng)性能正越來越嚴(yán)重地成為整個電腦系統(tǒng)性能提升的瓶頸。雖然磁盤外部接口也從以前的ATA33發(fā)展到今天的SATA 6Gbit/s。但是,這還是不能徹底解決磁盤瓶頸的問題,特別是在運(yùn)行一些對數(shù)據(jù)存取速度要求很高的程序,如數(shù)字影像處理或玩3D游戲裝入紋理數(shù)據(jù)時,受磁盤存取速度的影響,屏幕畫面時常會出現(xiàn)延遲和停頓。于是,虛擬磁盤技術(shù)(Ramdisk)應(yīng)運(yùn)而生,它可解上述問題的“燃眉之急”。
3 ?Ramdisk的特點(diǎn)
Ramdisk是基于內(nèi)存的塊設(shè)備,以內(nèi)存作為實(shí)際的存儲介質(zhì),但以塊設(shè)備的方式組織,所以它具有比實(shí)際磁盤更快的存取速度。但這也帶來了另一個問題,當(dāng)系統(tǒng)重啟,內(nèi)存掉電后,Ramdisk中存儲的數(shù)據(jù)也將會隨之消失。所以Ramdisk不適合作為長期保存文件的介質(zhì)。[2]
4? Ramdisk的作用
Ramdisk磁盤對于保存加密數(shù)據(jù)來說是一個福音,因?yàn)槲覀內(nèi)绻麑⒓用艿奈募饷艿狡胀ǖ拇疟P的話,即使我們隨后刪除了解密文件,數(shù)據(jù)仍然會留在磁盤上。這樣是非常不安全的。而對于Ramdiak來說,就不存在這樣的問題。另外,假設(shè)有幾個文件要頻繁的使用,你如果將它們加到內(nèi)存當(dāng)中,程序運(yùn)行速度會大幅提高,這是由存儲介質(zhì)的特性決定的(因?yàn)閮?nèi)存的讀寫速度遠(yuǎn)高于硬盤)。像Web服務(wù)器這樣的計(jì)算機(jī),需要大量的讀取和交換特定的文件,因此,在Web服務(wù)器上建立Ramdisk會大大提高網(wǎng)絡(luò)讀取的速度。
5 ?主要代碼的解釋
在程序的開始,首先定義了幾個宏,用于Ramdisk驅(qū)動中用到的一些變量的統(tǒng)一賦值:
#define GAO_RD_DEV_NAME "gao_rd" //設(shè)備名稱#define GAO_RD_DEV_MAJOR 220 //主設(shè)備號#define GAO_RD_MAX_DEVICE 2 //最大設(shè)備數(shù)#define GAO_BLOCKSIZE 1024 //塊大小#define GAO_RD_SECTOR_SIZE 512 //扇區(qū)大小#define GAO_RD_SIZE (4*1024*1024) //總大小#define GAO_RD_SECTOR_TOTAL (GAO_RD_SIZE/GAO_RD_SECTOR_SIZE) //扇區(qū)數(shù)
這些宏變量描述了驅(qū)動程序的一些基本的屬性和參數(shù),是Ramdisk的基本信息。
本驅(qū)動程序主要完成了如下幾個函數(shù):
1)??驅(qū)動模塊的初始化函數(shù)?
int gao_rd_init(void);//初始化
此函數(shù)主要完成驅(qū)動模塊的初始化和必要資源分配的工作。首先,為虛擬磁盤分配必要的內(nèi)存空間,用作它的存儲空間。然后用設(shè)備號和設(shè)備的名稱將此塊設(shè)備注冊到內(nèi)核中去。接下來要完成的任務(wù)就是分配通用磁盤結(jié)構(gòu)體gendisk并賦上相應(yīng)的值,分配請求隊(duì)列以及幫定此設(shè)備的制造請求函數(shù)和請求隊(duì)列。最后將通用磁盤結(jié)構(gòu)體gendisk添加到內(nèi)核中的相關(guān)隊(duì)列里。
代碼如下:
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設(shè)備驅(qū)動程序*/ if(register_blkdev(GAO_RD_DEV_MAJOR, GAO_RD_DEV_NAME))/*對此塊設(shè)備進(jìn)行注冊*/ { err = -EIO; goto out; } for(i = 0; i < GAO_RD_MAX_DEVICE; i++) { device[i].data = vdisk[i]; /*分配gendisk結(jié)構(gòu)題,gendisk結(jié)構(gòu)題是注冊會設(shè)備的信息結(jié)構(gòu)體*/ device[i].gd = alloc_disk(1); if (!device[i].gd) goto out; device[i].queue = blk_alloc_queue(GFP_KERNEL);// 分配正常的內(nèi)核 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;//塊設(shè)備操作結(jié)構(gòu)體 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?驅(qū)動模塊的卸載函數(shù)?
void gao_rd_exit(void);//模塊卸載函數(shù)
此函數(shù)完成與初始化函數(shù)相反的操作。它會刪除此驅(qū)動模塊被分配的通用磁盤結(jié)構(gòu)體,利用設(shè)備號和設(shè)備名稱刪除對此設(shè)備的注冊并釋放此設(shè)備曾占用的存儲空間。
代碼如下:
void gao_rd_exit(void){ int i; for(i = 0; i < GAO_RD_MAX_DEVICE; i++) { del_gendisk(device[i].gd);//刪除gendisk結(jié)構(gòu)體 put_disk(device[i].gd);//減少gendisk結(jié)構(gòu)體的引用計(jì)數(shù) 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??驅(qū)動模塊的制造請求函數(shù)?
static int gao_rd_make_request(struct request_queue *q, struct bio *bio);//制造請求函數(shù)
此函數(shù)處理設(shè)備使用過程中的實(shí)際I/O請求。
它會通過bio結(jié)構(gòu)體獲取此次I/O請求的相關(guān)信息,例如,存取標(biāo)志、存取位置、請求隊(duì)列等必要的I/O信息。之后遍歷請求隊(duì)列中的每一個段,如果是讀數(shù)據(jù)請求,便將指定位置的數(shù)據(jù)拷貝到緩沖區(qū)中;如果是寫數(shù)據(jù)請求,便將緩沖區(qū)的數(shù)據(jù)拷貝到指定的位置上。
代碼如下:
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)/*循環(huán)遍歷每一個段*/ { 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);//取消內(nèi)存頁地址映射 pVHDDData += bvec->bv_len; } /*結(jié)束處理,并終止gao_rd_make_request函數(shù)*/ bio_endio(bio,0); return 0; } }
4、編譯驅(qū)動模塊
要編譯此驅(qū)動模塊需要先編寫一個Makefile文件:
KERNELDIR = /usr/src/kernels/2.6.27.10-1-i686/ #指定內(nèi)核路徑 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終端下執(zhí)行make命令進(jìn)行編譯:
[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
現(xiàn)在,在目錄下回有一個gao_rd.ko的文件,這個就是編譯出來的可以加載的模塊文件。
用insmod命令加載此模塊后,在/proc/modules文件里就會看到此模塊已被加載。
5、測試驅(qū)動模塊
此驅(qū)動模塊被加載到內(nèi)核中后就可以利用此模塊創(chuàng)建一個虛擬磁盤設(shè)備了。主要步驟如下:
1)#mkdir? /root/Desktop/ramdisk/gao_rd
這個命令的作用是創(chuàng)建一個文件夾,我們用這個文件夾作為虛擬磁盤設(shè)備的掛載點(diǎn)。?????
2)#mknod? /dev/gao_rd0 b 220 0
創(chuàng)建一個塊設(shè)備,指定塊設(shè)備的主設(shè)備號是220,次設(shè)備號自動分配。現(xiàn)在,在/dev目錄下就會多出一個塊設(shè)備,名為gao_rd0。
3)#mke2fs /dev/gao_rd0
用ext2格式對此設(shè)備進(jìn)行格式化。
至此,就完成了一個虛擬磁盤設(shè)備的創(chuàng)建。
4)#mount /dev/gao_rd0 /root/Desktop/ramdisk/gao_rd
這個命令的作用是將剛剛創(chuàng)建的虛擬磁盤設(shè)備掛載到第一步創(chuàng)建的掛載點(diǎn)上。這樣,這個虛擬磁盤就可以使用了。我們可以用ls命令來查看這塊虛擬磁盤設(shè)備。?
以下是完整的代碼:
#include #include #include //定義了一些常用的函數(shù)原型#include //#include //一些出錯的常量符號的宏#include //定義了一些基本的數(shù)據(jù)類型。所有類型均定義為適當(dāng)?shù)臄?shù)字類型長度。#include //文件控制選項(xiàng)頭文件,#include #include //定義了一些對硬盤控制器進(jìn)行編程的一些命令常量符號。#include #include #include /*設(shè)備名稱,段大小,設(shè)備大小等信息的定義*/#define GAO_RD_DEV_NAME "gao_rd" //設(shè)備名稱#define GAO_RD_DEV_MAJOR 220 //主設(shè)備號#define GAO_RD_MAX_DEVICE 2 //最大設(shè)備數(shù)#define GAO_BLOCKSIZE 1024#define GAO_RD_SECTOR_SIZE 512 //扇區(qū)大小#define GAO_RD_SIZE (4*1024*1024) //總大小#define GAO_RD_SECTOR_TOTAL (GAO_RD_SIZE/GAO_RD_SECTOR_SIZE) //總扇區(qū)數(shù)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)/*制造請求函數(shù)*/{ 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)/*循環(huán)遍歷的宏*/ { pBuffer = kmap(bvec->bv_page) + bvec-> bv_offset;//kmap()函數(shù)??? 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; } /*結(jié)束處理,并終止gao_rd_make_request函數(shù)*/ 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;//不適當(dāng)?shù)腎/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 結(jié)構(gòu)體是對塊設(shè)備操作的集合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設(shè)備驅(qū)動程序*/ if(register_blkdev(GAO_RD_DEV_MAJOR, GAO_RD_DEV_NAME))//對此塊設(shè)備進(jìn)行注冊 { err = -EIO; goto out; } /**/ for(i = 0; i < GAO_RD_MAX_DEVICE; i++) { device[i].data = vdisk[i]; /*分配gendisk結(jié)構(gòu)題,gendisk結(jié)構(gòu)題是注冊會設(shè)備的信息結(jié)構(gòu)體*/ device[i].gd = alloc_disk(1); if (!device[i].gd) goto out; device[i].queue = blk_alloc_queue(GFP_KERNEL);//GFP_KERNEL 分配正常的內(nèi)核 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;//塊設(shè)備操作結(jié)構(gòu)體 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結(jié)構(gòu)體 put_disk(device[i].gd);//減少gendisk結(jié)構(gòu)體的引用計(jì)數(shù) 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
?
評論
查看更多