Linux驅動開發----塊裝置驅動(記憶體模擬)Tiny6410
阿新 • • 發佈:2019-01-07
寫了好久的字元裝置驅動,是時候看下塊裝置驅動程式設計方法了,塊裝置驅動和字元裝置不同,字元裝置是直接和虛擬檔案系統進行互動,而塊裝置驅動則是通過塊緩衝/排程層間接和虛擬檔案系統互動;塊裝置驅動資料訪問都是以塊為單位;多個塊I/O需要組成一個請求佇列,這個功能是塊緩衝/排程層提供的,它出於硬體特性和讀寫效能的考慮,將塊I/O進行重新排序,並組成一個請求佇列,交給核心,核心則呼叫請求佇列處理函式來逐個處理請求佇列。
大致框架:
1.分配gendisk結構,使用alloc_disk函式
2.分配設定請求佇列,提供讀寫能力
3.設定其他資訊,例如容量、名稱等
4.硬體相關操作
5.註冊(add_disk)
在測試塊裝置驅動的過程中,剛開始使用的是函式kzmalloc申請1M大小的記憶體來進行測試,可是驅動載入的時候沒有問題,可是當使用mkdosfs格式化的時候報錯"short write".,並且在匯出到檔案上時也提示錯誤,核心直接崩潰掉。後來改成vmalloc函式就可以了。額。。這是為什麼??還沒弄明白。。
下面是程式碼:
#include <linux/init.h>//module_init/exit #include <linux/module.h>//MODULE_AUTHOR,MODULE_LICENSE等 #include <linux/genhd.h>//alloc_disk #include <linux/blkdev.h>//blk_init_queue #include <linux/fs.h>//register_blkdev,unregister_blkdev #include <linux/types.h>//u_char,u_short #include <linux/vmalloc.h> #include <linux/hdreg.h> //1MB大小空間 #define RAMBLK_SIZE (1024*1024*2) /* //定義驅動私有資料結構 struct ramblk_info{ u_char heads;//磁頭數 u_short cylinders;//柱面數 u_char sectors;//扇區數 u_char control; int unit; }; */ static struct gendisk * ramblk_disk = NULL; static struct request_queue * ramblk_request_queue = NULL; static int major = 0;//塊裝置的主裝置號 //static struct ramblk_info *pinfo = NULL; static DEFINE_SPINLOCK(ramblk_spinlock);//定義並初始化一個自旋鎖 static char * ramblk_buf = NULL;//申請的記憶體起始地址 //ramdisk_info初始化函式 /* static int ramblk_info_init(struct ramblk_info *p) { if(!p){ printk("ramblk_info_init p==NULL.\n"); return -1; } p->heads = 4;//4個磁頭 p->cylinders = 4;//4個柱面 p->sectors = 128;//128個扇區 return 0; } */ int ramblk_getgeo(struct block_device * blk_Dev, struct hd_geometry * hg) { hg->cylinders = 64; hg->heads = 8; hg->sectors = (RAMBLK_SIZE/8/64/512); return 0; } static const struct block_device_operations ramblk_fops = { .owner = THIS_MODULE, .getgeo = ramblk_getgeo, }; static void do_ramblk_request(struct request_queue *q ) { struct request *req; // static volatile int r_cnt = 0; // static volatile int w_cnt = 0; //printk("ramblk_request_fn %d.\n",cnt++); req = blk_fetch_request(q); while (req) { unsigned long start = blk_rq_pos(req) << 9; unsigned long len = blk_rq_cur_bytes(req); // printk("len=%d.\n",len); if (start + len > RAMBLK_SIZE) { printk("RAMBLK_SIZE< start+len"); goto done; } if (rq_data_dir(req) == READ) memcpy(req->buffer, (char *)(start+ramblk_buf), len); else memcpy((char *)(start+ramblk_buf), req->buffer, len); done: if (!__blk_end_request_cur(req, 0)) req = blk_fetch_request(q); } } static int ramblk_init(void) { // 1.分配gendisk結構體,使用alloc_disk函式 ramblk_disk = alloc_disk(16);//minors=分割槽+1 // 2.設定 // 2.1 分配/設定佇列,提供讀寫能力.使用函式blk_init_queue(request_fn_proc *rfn,spin_lock_t *lock) ramblk_request_queue = blk_init_queue(do_ramblk_request,&ramblk_spinlock); // 2.2 設定disk的其他資訊,比如容量、主裝置號等 major = register_blkdev(0,"ramblk");//註冊主裝置 if(major < 0){//檢查是否成功分配一個有效的主裝置號 printk(KERN_ALERT "register_blkdev err.\n"); return -1; } //設定主裝置號 ramblk_disk->major = major; ramblk_disk->first_minor = 0;//設定第一個次裝置號 sprintf(ramblk_disk->disk_name, "ramblk%c", 'a');//設定裝置名 ramblk_disk->fops = &ramblk_fops;//設定fops /* //分配一個ramdisk_info結構體,並初始化 pinfo =(struct ramblk_info*)kmalloc(sizeof(struct ramblk_info),GFP_KERNEL); if(!pinfo){ printk("kmalloc pinfo err.\n"); return -1; } ramblk_info_init(pinfo); ramlk_disk->private_data = pinfo;*/ ramblk_disk->queue = ramblk_request_queue;//設定請求佇列 set_capacity(ramblk_disk, RAMBLK_SIZE/512);//設定容量 // 3.硬體相關的操作 ramblk_buf = (char*)vmalloc(RAMBLK_SIZE);//申請RAMBLK_SIZE記憶體 // 4.註冊 add_disk(ramblk_disk);//add partitioning information to kernel list printk("ramblk_init.\n"); return 0; } static void ramblk_exit(void) { unregister_blkdev(major,"ramblk");//登出裝置驅動 blk_cleanup_queue(ramblk_request_queue);//清除佇列 del_gendisk(ramblk_disk); put_disk(ramblk_disk); vfree(ramblk_buf);//釋放申請的記憶體 printk("ramblk_exit.\n"); } module_init(ramblk_init);//入口 module_exit(ramblk_exit);//出口 MODULE_AUTHOR("jefby"); MODULE_LICENSE("Dual BSD/GPL");
編譯完成後,使用mkdosfs格式化,並掛載到目錄test下,讀寫檔案,重新掛載檢視檔案是否存在,另外,可以匯出到檔案上,在PC機上測試檔案是否正確。