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
?
評論