linux 塊裝置驅動(1)
阿新 • • 發佈:2019-01-05
功能:新增一個ramdisk這樣的裝置,並且進行格式化,掛在,讀寫,很簡單的一個塊裝置例子,剛剛學習裝置驅動
<font color="#ff0000" size="5">看完,並且執行完程式碼,希望大家和我有一樣的問題,就是新的裝置空間那裡來的?原來的硬碟?還是記憶體,快取?</font>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/genhd.h>
#include<linux/blkdev.h>
#include<linux/bio.h>
#define DEVICE_MAJOR 240
#define DEVICE_NAME "ramdisk"
#define SECTOR_SIZE 512 //扇區大小
#define DISK_SIZE (3*1024*1024) //虛擬磁碟大小
#define SECTOR_ALL (DISK_SIZE/SECTOR_SIZE) //虛擬磁碟的扇區數
static struct gendisk *p_disk; //用來指向申請的gendisk結構體
static struct request_queue *p_queue; //用來指向請求佇列
static unsigned char mem_start[DISK_SIZE]; //分配一塊3M 的記憶體作為虛擬磁碟
/***********************************************************************
對於ram、loop、網路裝置等使用自己編寫的make_request函式來處理bio
省去使用核心I/O排程器的過程,make_request返回值是0
**********************************************************************/
static int ramdisk_make_request(struct request_queue *q,struct bio *bio)
{
struct bio_vec *bvec; //bio結構中包含多個bio_vec結構
int i; //用於迴圈的變數,不需要賦值
void *disk_mem; //指向虛擬磁碟正在讀寫的位置
if((bio->bi_sector*SECTOR_SIZE)+bio->bi_size>DISK_SIZE){ //檢查超出容量的情況
printk("ramdisk over flowed!\n");
bio_endio(bio,1); //第二個引數為1通知核心bio處理出錯
return 0;
}
disk_mem=mem_start+bio->bi_sector*SECTOR_SIZE; //mem_start是虛擬磁碟的起始地址
//bio_for_each_segment是一個for迴圈的巨集,每次迴圈處理一個bio_vec
bio_for_each_segment(bvec,bio,i){
void *iovec; //指向核心存放資料的地址
iovec=kmap(bvec->bv_page)+bvec->bv_offset; //將bv_page對映到高階記憶體
switch(bio_data_dir(bio)){ //bio_data_dir(bio)返回要處理資料的方向
case READA: //READA是預讀,RAED是讀,採用同一處理
case READ:memcpy(iovec,disk_mem,bvec->bv_len);break;
case WRITE:memcpy(disk_mem,iovec,bvec->bv_len);break;
default:bio_endio(bio,1);kunmap(bvec->bv_page);return 0; //處理失敗的情況
}
kunmap(bvec->bv_page); //釋放bv_page的對映
disk_mem+=bvec->bv_len; //移動虛擬磁碟的指向位置,準備下一個迴圈bvec的讀寫做準備
}
bio_endio(bio,0); //第二個引數為0通知核心處理成功
return 0;
}
static struct block_device_operations ramdisk_fops={
.owner=THIS_MODULE,
};
static int ramdisk_init(void)
{
p_queue=blk_alloc_queue(GFP_KERNEL); //申請請求佇列,不附加make_request函式
if(!p_queue)return -1;
blk_queue_make_request(p_queue,ramdisk_make_request); //將自己編寫的make_request函式新增到申請的佇列
p_disk=alloc_disk(1); //申請一個分割槽的gendisk結構體
if(!p_disk){
blk_cleanup_queue(p_queue); //gendisk申請失敗,清除已申請的請求佇列
return -1;
}
strcpy(p_disk->disk_name,DEVICE_NAME); //塊裝置名
p_disk->major=DEVICE_MAJOR; //主裝置號
p_disk->first_minor=0; //次裝置號
p_disk->fops=&ramdisk_fops; //fops地址
p_disk->queue=p_queue; //請求佇列地址
set_capacity(p_disk,SECTOR_ALL); //設定磁碟扇區數
add_disk(p_disk); //設定好後新增這個磁碟
return 0;
}
static void ramdisk_exit(void)
{
del_gendisk(p_disk); //刪除gendisk註冊資訊
put_disk(p_disk); //釋放disk空間
blk_cleanup_queue(p_queue); //清除請求佇列
}
module_init(ramdisk_init);
module_exit(ramdisk_exit);
MODULE_LICENSE("GPL");
編譯成模組按下列步驟實驗一下:
insmod ramdisk_driver.ko //動態掛載驅動模組
lsmod //檢視是否多了個ramdisk_driver模組
ls /dev //檢視一下/dev目錄下是否多了個ramdisk節點
mkfs.ext3 /dev/ramdisk //在虛擬磁碟上建立ext3檔案系統
mount /dev/ramdisk /mnt/test //掛載到/mnt/test目錄下,然後去看看mnt下面是不是多了lost+found資料夾
終端敲入mount,看一下mount的記錄,是不是ext3格式
玩夠了之後就清除掉
umount /mnt/test
rmmod ramdisk_driver