如何写块设备驱动程序
1., 使用register_blkdev()创建一个块设备
2,以面向对象的思想分配 gendisk 结构体。用 alloc_disk 函数。
3,设置 gendisk 结构体。
①,分配/设置一个队列:request_queue_t. (提供读写能力)用 blk_init_queue 函
数。
②,设置 gendisk 其他信息。(提供磁盘属性:磁盘容量,扇区大小等)
4,注册 gendisk 结构体。用 add_disk 函数
/*13.块驱动程序:分配一段内存,用内存来模拟硬盘:*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define RAMBLOCK_SIZE (1024*1024) //定义块设备的容量为1M字节
static struct gendisk *ramblock_disk; //定义gendisk通用磁盘结构体
static struct request_queue *ramblock_queue; //定义请求队列结构体
static int major; //定义主设备号
static DEFINE_SPINLOCK(ramblock_lock); //定义一个自旋锁,用于分配/设置队列
static struct block_device_operations ramblock_fops = {
.owner = THIS_MODULE,
};
static void do_ramblock_request (request_queue_t * q)
{
}
static int ramblock_init(void)
{
/*1.分配一个gendisk(通用磁盘)结构体*/
ramblock_disk = alloc_disk(16); //次设备号个数:分区个数+1
/*2.设置*/
/*2.1 分配/设置一个队列request_queue_t. (提供读写能力)*/
ramblock_queue=blk_init_queue(do_ramblock_request, &ramblock_lock); //分配队列
ramblock_disk->queue = ramblock_queue; //设置队列
/*2.2设置gendisk其他属性:容量等*/
major = register_blkdev(0, "ramblock"); //创建一个块设备,当major==0时,表示动态创建,创建成功会返回一个主设备号,可通过 cat 、proc/device查看到
ramblock_disk->major = major; //主设备号
ramblock_disk->first_minor = 0; ///第一个次设备号写为0,则从0~16都对应这个块设备。
sprintf(ramblock_disk->disk_name, "ramblock"); //块设备的名字
ramblock_disk->fops = &ramblock_fops; //必要的操作函数,即使为空也要提供该结构体
// ramblock_disk->private_data = p; 私有数据不需要
set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512); //设置容量,单位为扇区个数,扇区=512字节
/*3.注册gendisk结构体,用add_disk函数*/
add_disk(ramblock_disk);
return 0;
}
static void rmablock_exit(void)
{
unregister_blkdev(major, "ramblock"); //卸载块设备
//使用put_disk()和del_gendisk()来注销,释放gendisk结构体
put_disk(ramblock_disk);
del_gendisk(ramblock_disk);
blk_cleanup_queue(ramblock_queue); //清除内存中的申请队列
}
module_init(ramblock_init);
module_exit(rmablock_exit);
MODULE_LICENSE("GPL");
ramblock_disk= allock_disk(16);
alloc_disk (int minors)需要参数“minor+s”是指次设备号个数。即“分区个数
+1”。minors为 1 时,就是把整个磁盘当成一个分区,则在上面不能再创建分
区了。如写成 16,则最多可以创建 15 个分区。
若是说,这个主设备号之下,从哪个次设备号开始都是对应这个块设备。
对于一个块设备,次设备号为“0”时,表示整个磁盘。如“/dev/sda”。次设备号“1,
2,、”表示是磁盘的第几个 主 分区。次设备号从 5 开始是“扩展分区”。
ramblock_queue = blk_init_queue (do_rambloc_request, &ramblock_lock);
blk_init_queue (request_fn_proc * rfn, spinlock_t * lock) 分配一个队列。
参 1 rfn,执行处理队列的函数。
参 2 lock,一个“自旋锁”。 需提前定义:DEFINE_SPINLOCK (beep_lock)
函数功能:分配一个请求队列,分配成功则返回一个队列结构体
提供读写能力:之前分析把“文件读写”转成“扇区读写”,对“扇区的读写”会放入个队列里面
使用register_blkdev()创建一个块设备*/
major = register_blkdev(0, "ramblock"); //创建一个块设备,当major=0时,表示动态创建,创建成功会返回一个主设备号,可通过 cat 、proc/device查看到
1. ramblock_disk->major = major; //主设备号
2 ramblock_disk->first_minor = 0; ///第一个次设备号,写为0则从0~16都对应这个块设备。
3. sprintf(ramblock_disk->disk_name, "ramblock"); //块设备的名字
4. ramblock_disk->fops = &ramblock_fops; //必要的操作函数,即使为空也要提供该结构体
5. set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512); //容量,单位为扇区个数,扇区=512字节
1.主设备号:
static int major;
major = register_blkdev(0, “ramblock”); //自动分配主设备号
ramblock_disk->major = major; //属性:主设备号
主设备号可以自已定义,也可以让系统自动分配。
2.,第一个次设备号 和 块设备的名字。
ramblock_disk->first_minor = 0; //第一个次设备号写为0,则从0~16都对应这个块设备。
printf(ramblock_disk->disk_name, “ramblock”); //块设备的名字
3.fops :操作函数。即使是空的操作函数,这个 fops 也要提供。
static struct block_device_operations ramblock_fops = {
.owner = THIS_MODULE,
.ioctl = ramblock_ioctl,
.getgeo = ramblock_getgeo,
};
经过实验,即使这个函数里什么也没有(如没有".ioctl"和".getgeo"),也要提供这个结构体。不然会出错。
ramblock_disk->fops = &ramblock_fops; //必要的操作函数
4.设置队列:
把设置队列放在“分配队列”那一步骤代码紧跟的之下。就是把队列使用起来,即放到
request_queue_t 结构变量 ramblock_queue 里中去。
//2.1,分配/设置队列:提供读写能力
ramblock_queue = blk_init_queue (do_rambloc_request, &ramblock_lock);
ramblock_disk->queue = ramblock_queue; //设置队列
5.容量:设置容量时,是以扇区为单位。
块设备容量 1M 字节
#define RAMBLOCK_SIZE (1024*1024)
set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);
单位是“扇区”,在内核里,对于文件系统那一层,永远认为扇区是 512 字节。
6.处理请求函数:
在“分配/设置队列 blk_init_queue()”函数中,形参 1“request_fn_proc *rfn”是用
来“处理”队列中的请求的函数。
//定义"处理队列请求"的函数
static void do_rambloc_request (request_queue_t * q)
{
}
这里什么也没做,以后慢慢完善。以上一个驱动程序基本上写完了。
add_disk(ramblock_disk);
三:出口函数
1.卸载块设备
unregister_blkdev(major, “ramblock”); //卸载块设备
2.使用put_disk()和del_gendisk()来注销,释放gendisk结构体
put_disk(ramblock_disk);
del_gendisk(ramblock_disk);
3./清除内存中的申请队列
blk_cleanup_queue(ramblock_queue); //清除内存中的申请队列
步骤如下:
1在入口函数中:
1)使用register_blkdev()创建一个块设备
2) blk_init_queue()使用分配一个申请队列,并赋申请队列处理函数
3)使用alloc_disk()分配一个gendisk结构体
4)设置gendisk结构体的成员
->4.1)设置成员参数(major、first_minor、disk_name、fops)
->4.2)设置queue成员,等于之前分配的申请队列
->4.3)通过set_capacity()设置capacity成员,等于扇区数
5)使用kzalloc()来获取缓存地址,用做扇区
6)使用add_disk()注册gendisk结构体
2在申请队列的处理函数中
3在出口函数中
1)使用put_disk()和del_gendisk()来注销,释放gendisk结构体
2)使用kfree()释放磁盘扇区缓存
3)使用blk_cleanup_queue()清除内存中的申请队列
4)使用unregister_blkdev()卸载块设备