寫了好久的字符設備驅動,是時候看下塊設備驅動程序設計方法了,塊設備驅動和字符設備不同,字符設備是直接和虛擬文件系統進行交互,而塊設備驅動則是通過塊緩沖/調度層間接和虛擬文件系統交互;塊設備驅動數據訪問都是以塊為單位;多個塊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機上測試文件是否正確。