本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。
作者:fireaxe.hq@outlook.com
博客:fireaxe.blog.chinaunix.net
1. 前言
本文实现了一个最简单的块设备驱动程序,块设备通过内存进行模拟。
块设备与字符设备的区别是什么?简单来讲,就是字符设备中间层非常薄,只需要为用户io的read、write、open等接口提供回调即可;快速设备则是先挂到block层,向上提供用户io的操作。而block层,则可以自由挂接各种文件系统。
对于有块设备驱动,需要了解的是需要向linux block层提供什么样的接口。
2. 内存虚拟块设备注册
(1)首先是申请块设备号,使用下面的接口:
int register_blkdev(unsigned int major, const char *name)
(2)然后通过下面的接口把驱动挂入block层。
void add_disk(struct gendisk *disk)
block对下看到了就是一个个gendisk,其中需要开发这设置的主要是如下的几个参数:
-
majoR:块设备号,来自于register_blkdev。
-
disk_name:设备名,未来从dev目录下看到的就是这个名字。
-
fops:用于直接操作块设备的接口,不添加也可以,因为通常都是通过block层下发request进行操作,不会直接使用该接口,但某些如geoget与ioctl接口,则需要通过这里挂接处理函数。本例子中没有添加。
-
private_data:通常用于存储驱动数据结构入口。当文件系统操作某文件时,会从这个参数找到需要操作的设备。
-
queue:这个是与block层交互的主要方式,是一个request_queue,block层的request都会被挂入这个queue,再进行处理。
(3)设备数据结构
设备数据结构用于维护设备相关的信息,这里只维护最基本的:设备大小与入口指针。
我们这里是用内存虚拟的块设备,因此入口指针就是vmalloc的返回值,大小就是vmalloc申请的大小。
我们认为的把这块内存划分成n个sector,
reques分解到最后时,对应的是对某一块sector的读写操作,我们根据sector的编号,使用这块内存对应的sector即可。
3. 数据流:request
request到达驱动时,会调用驱动注册的request queue处理函数进行处理,例程中有request的处理函数的实现,其核心是通过requestqueue,找到需要处理的数据。要理解这段代码,则需要了解request queue的结构。
应用程序对文件的read与write操作,会在block 层中生成request请求,并挂入对应设备的reqeust_queue队列,然后会触发驱动来处理request。这样做的目的是为了避免每次read与write都触发设备的读写。因为外设的读写比较耗时间,但每次读取时,读取一个sector与读取多个sector的速度相差不多。通过request queue,可以把多次请求合并成一次请求。
每个request中可以挂多个bio,每个bio中又可以有多个segment,每个segment中对应着一块或连续多块sector。驱动开发者添加为request queue添加的处理函数wiccablk_request就是为了找打所有需要处理的sector,一次性处理它们。为了方便编写,例程中按照这种层级关系实现了一串函数:
wiccablk_request() -> wiccablk_xfer_request() -> wiccablk_xfer_bio() -> wiccablk_transfer()
注:request的构成涉及到文件系统的实现原理,涉及到io请求合并、cache处理等内容。具体分析会在后续分析文件系统时写,这里只要理解request到sector的过称就可以了。
4. 测试
# 添加并格式化我们用内存虚拟的块设备
insmod test_wiccablk.ko
ls /dev/wiccablk*
mkfs.ext4 /dev/wiccablka
# 测一下是否能够像块设备一样使用
mount /dev/wiccablka abc
echo 123456 > abc/123
cat abc/123
umount abc
# umount后再次mount,看看原来的内容还在不在
mount /dev/wiccablka abc
cat abc/123
umount abc
5. 代码参考
-
/*basedonkernel 4.10*/
-
-
#include/*formodules*/
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include/*file_operations*/
-
#include/*copy_(to,from)_user*/
-
#include/*module_init,module_exit*/
-
#include/*kmalloc,kfree*/
-
#include/*kmalloc,kfree*/
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
-
#define KERNEL_SECTOR_SIZE 512
-
#define WICCA_MINOR_MAX 1
-
#define SECTOR_SHIFT 9
-
-
staticintwiccablk_major=0;
-
staticinthardsect_size=512;
-
staticintnsectors=1024;/*How big the driveis*/
-
staticintndevices=1;
-
-
struct wiccablk_dev{
-
intsize;
-
u8*data;
-
struct gendisk*gd;
-
};
-
-
struct wiccablk_dev*Devices;
-
-
static struct block_device_operations wiccablk_ops={
-
.owner=THIS_MODULE,
-
};
-
-
static void wiccablk_transfer(struct wiccablk_dev*dev,unsigned long sector,
-
unsigned long nsect,char*buffer,intwrite)
-
{
-
unsigned long offset=sector*hardsect_size;
-
unsigned long nbytes=nsect*hardsect_size;
-
-
if((offset+nbytes)>dev->size){
-
printk("wiccablk write (%ld %ld)\n",offset,nbytes);
-
return;
-
}
-
-
if(write)
-
memcpy(dev->data+offset,buffer,nbytes);
-
else
-
memcpy(buffer,dev->data+offset,nbytes);
-
}
-
-
staticintwiccablk_xfer_bio(struct wiccablk_dev*dev,struct bio*bio)
-
{
-
struct bvec_iter i;
-
struct bio_vec bvec;
-
sector_t sector=bio->bi_iter.bi_sector;
-
-
bio_for_each_segment(bvec,bio,i){
-
unsignedintnsect=bvec.bv_len/hardsect_size;
-
char*buffer=__bio_kmap_atomic(bio,i);
-
wiccablk_transfer(dev,sector,nsect,
-
buffer,bio_data_dir(bio)==WRITE);
-
sector+=nsect;
-
__bio_kunmap_atomic(bio);
-
}
-
return 0;
-
}
-
-
staticintwiccablk_xfer_request(struct wiccablk_dev*dev,struct request*req)
-
{
-
struct bio*bio;
-
intnsect=0;
-
-
__rq_for_each_bio(bio,req){
-
wiccablk_xfer_bio(dev,bio);
-
nsect+=bio->bi_iter.bi_size/hardsect_size;
-
}
-
return nsect;
-
}
-
-
static void wiccablk_request(struct request_queue*q)
-
{
-
struct request*req;
-
struct wiccablk_dev*dev=q->queuedata;
-
-
req=blk_fetch_request(q);
-
while(req!=NULL){
-
wiccablk_xfer_request(dev,req);
-
if(!__blk_end_request_cur(req,0))
-
req=blk_fetch_request(q);
-
}
-
}
-
-
static void setup_device(struct wiccablk_dev*dev,intwhich)
-
{
-
struct request_queue*queue;
-
-
memset(dev,0,sizeof(struct wiccablk_dev));
-
dev->size=nsectors*hardsect_size;
-
-
dev->data=vmalloc(dev->size);
-
if(dev->data==NULL){
-
printk("wiccablk vmalloc failure\n");
-
return;
-
}
-
-
dev->gd=alloc_disk(WICCA_MINOR_MAX);
-
if(!dev->gd){
-
printk("wiccablk alloc_disk failed\n");
-
vfree(dev->data);
-
return;
-
}
-
-
queue=blk_init_queue(wiccablk_request,NULL);
-
if(queue==NULL){
-
printk("wiccablk blk_init_queue failed\n");
-
del_gendisk(dev->gd);
-
vfree(dev->data);
-
return;
-
}
-
-
blk_queue_logical_block_size(queue,hardsect_size);
-
queue->queuedata=dev;
-
dev->gd->queue=queue;
-
-
dev->gd->major=wiccablk_major;
-
dev->gd->first_minor=which*WICCA_MINOR_MAX;
-
dev->gd->fops=&wiccablk_ops;
-
dev->gd->private_data=dev;
-
-
snprintf(dev->gd->disk_name,32,"wiccablk%c",which+'a');
-
set_capacity(dev->gd,nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
-
printk("wiccablk add_disk %s\n",dev->gd->disk_name);
-
add_disk(dev->gd);
-
}
-
-
staticint__init init_wiccablk(void)
-
{
-
inti;
-
-
wiccablk_major=register_blkdev(0,"wiccablk");
-
if(wiccablk_major<=0){
-
printk("wiccablk register_blkdev failed\n");
-
return-EBUSY;
-
}
-
-
Devices=kmalloc(ndevices*sizeof(struct wiccablk_dev),GFP_KERNEL);
-
if(Devices==NULL){
-
goto out_unregister;
-
}
-
-
for(i=0;i
-
printk("wiccablk setup_device %d\n",i);
-
setup_device(Devices+i,i);
-
}
-
-
printk("test_wiccablk init ok\n");
-
-
return 0;/*everythingisok*/
-
-
out_unregister:
-
unregister_blkdev(wiccablk_major,"wiccablk");
-
return-ENOMEM;
-
}
-
-
static void __exit cleanup_wiccablk(void)
-
{
-
inti;
-
-
for(i=0;i
-
struct wiccablk_dev*dev=Devices+i;
-
-
blk_cleanup_queue(dev->gd->queue);
-
del_gendisk(dev->gd);
-
vfree(dev->data);
-
}
-
-
unregister_blkdev(wiccablk_major,"wiccablk");
-
kfree(Devices);
-
-
printk("test_wiccablk exit ok\n");
-
}
-
-
module_init(init_wiccablk);
-
module_exit(cleanup_wiccablk);
-
-
MODULE_LICENSE("GPL");
-
MODULE_AUTHOR("Marco Hao");
本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。
作者:fireaxe.hq@outlook.com
博客:fireaxe.blog.chinaunix.net
阅读(3941) | 评论(0) | 转发(0) |