1. 程式人生 > >linux 塊裝置驅動(1)

linux 塊裝置驅動(1)

功能:新增一個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