块设备驱动程序分析

字符设备三部曲:

    alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello");   
    cdev_init(&hello_cdev, &hello_fops);  
    cdev_add(&hello_cdev, devid, HELLO_CNT); 

块设备:

    major = register_blkdev(0, "ramblock");

    struct gendisk ramblock_dis = alloc_disk(5);

    add_disk(ramblock_dis);

    如此看来,块设备驱动与字符设备驱动程序的编写流程是相似的,对于字符设备来说经过三部曲已经足够了,它主要目的就是绑定 file_operation 结构,应用程序能够经过系统调用调用到 file_operation 中函数就可以了。然而块设备要复杂的多,因为块设备的读写特性,导致我们需要为每一个块设备提供与一个请求队列,将内核挂起的I/O请求合并排序之后加入请求队列,请求队列还需要具有处理请求的能力。那么,整个块设备驱动程序框架大致分为以下四部分:

    1、分配 gendisk 结构体

      ramblock_dis = alloc_disk

    2、构造一个 request_queue 请求队列

      ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);

      blk_init_queue 会初始化一个请求队列,并设置它的默认“构造请求函数”__make_request

     __make_request 将内核挂起的请求能合并合并不能合并插入到队列尾

        // 尝试合并

        elv_merge(q, &req, bio);
        // 如果合并不成,使用bio构造请求
        init_request_from_bio(req, bio);
        // 把请求放入队列
        add_request(q, req);
        // 执行队列
        __generic_unplug_device(q);
        // 调用队列的"处理函数"
        q->request_fn(q);

      q->request_fn 是 blk_init_queue 时提供的请求处理函数,例如:do_ramblock_request

      ramblock_lock 是一个自旋锁:static DEFINE_SPINLOCK(ramblock_lock);

    3、填充 gendisk

      ramblock_dis->queue = ramblock_queue; // 绑定请求队列到 gendisk
      major = register_blkdev(0, "ramblock");
      ramblock_dis->major = major;
      ramblock_dis->first_minor = 0; 
      sprintf(ramblock_dis->disk_name, "ramblock");
      ramblock_dis->fops = &ramblock_fops;
      set_capacity(ramblock_dis, RAMBLOCK_SIZE/512);//扇区个数

    4、注册

      add_disk(ramblock_dis);


简单看一下 alloc_disk 过程和 add_disk 过程:

struct gendisk *alloc_disk(int minors)
{
	return alloc_disk_node(minors, -1);
}
EXPORT_SYMBOL(alloc_disk);

struct gendisk *alloc_disk_node(int minors, int node_id)
{
	struct gendisk *disk;

	disk = kmalloc_node(sizeof(struct gendisk),
				GFP_KERNEL | __GFP_ZERO, node_id);
	if (disk) {

		disk->node_id = node_id;

		disk->part_tbl->part[0] = &disk->part0;
		// 分区数量 + 1
		disk->minors = minors;
		// disk->random = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
		rand_initialize_disk(disk);
		// 块设备类 disk->part0._dev.class = &block_class;
		disk_to_dev(disk)->class = &block_class;
		// 块设备属性
		disk_to_dev(disk)->type = &disk_type;
		// 初始化 device (之后 device_add)
		device_initialize(disk_to_dev(disk));
		INIT_WORK(&disk->async_notify,
			media_change_notify_thread);
	}
	return disk;
}
    所有的设备在内核中都得有一个 device 结构来描述,块设备也一样,它属于块设备类。
void add_disk(struct gendisk *disk)
{
	struct backing_dev_info *bdi;
	dev_t devt;
	int retval;


	disk->flags |= GENHD_FL_UP;
	// *devt = MKDEV(disk->major, disk->first_minor + part->partno = 0);
	retval = blk_alloc_devt(&disk->part0, &devt);
	// disk->part0._dev.devt = devt;设备号
	disk_to_dev(disk)->devt = devt;

	disk->major = MAJOR(devt);
	disk->first_minor = MINOR(devt);
	
	// kobj_map(bdev_map, devt, range, module, probe, lock, data);
	blk_register_region(disk_devt(disk), disk->minors, NULL,
			    exact_match, exact_lock, disk);
	
	register_disk(disk);
	blk_register_queue(disk);

	bdi = &disk->queue->backing_dev_info;
	bdi_register_dev(bdi, disk_devt(disk));
	retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,
				   "bdi");
	WARN_ON(retval);
}

void register_disk(struct gendisk *disk)
{
	struct device *ddev = disk_to_dev(disk);
	struct block_device *bdev;
	struct disk_part_iter piter;
	struct hd_struct *part;
	int err;

	ddev->parent = disk->driverfs_dev;

	dev_set_name(ddev, disk->disk_name);

	/* delay uevents, until we scanned partition table */
	dev_set_uevent_suppress(ddev, 1);
	// 注册设备
	if (device_add(ddev))
		return;
#ifndef CONFIG_SYSFS_DEPRECATED
	err = sysfs_create_link(block_depr, &ddev->kobj,
				kobject_name(&ddev->kobj));
	if (err) {
		device_del(ddev);
		return;
	}
#endif
	disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj);
	disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj);

	// bdev = bdget(part_devt(part)); 获得 block_device 
	// list_add(&bdev->bd_list, &all_bdevs);
	bdev = bdget_disk(disk, 0);

	bdev->bd_invalidated = 1;
	err = blkdev_get(bdev, FMODE_READ);

	blkdev_put(bdev, FMODE_READ);

exit:
	/* announce disk after possible partitions are created */
	dev_set_uevent_suppress(ddev, 0);
	kobject_uevent(&ddev->kobj, KOBJ_ADD);

	/* announce possible partitions */
	disk_part_iter_init(&piter, disk, 0);
	while ((part = disk_part_iter_next(&piter)))
		kobject_uevent(&part_to_dev(part)->kobj, KOBJ_ADD);
	disk_part_iter_exit(&piter);
}
    经过以上过程,块设备就被注册到内核中去了。我们看到有一个全局的链表 all_bdevs,每一个注册的 block_device 都被添加到其中。

    有了驱动程序之后,我们在应用层读写文件,由文件系统转化为对扇区的读写,也就是调用 ll_rw_block , ll_rw_block 就会找到该请求对应的块设备,找到块设备的请求队列,利用请求队列的构造请求函数来将请求合并排队添加到请求队列中去,然后利用请求队列的请求处理函数处理请求。

    应用 -> VFS -> 具体的文件系统 ll_ll_rw_block -> 块设备驱动 -> 硬件


内存模拟块设备:

/* 参考:
 * drivers\block\xd.c
 * drivers\block\z2ram.c
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>

#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>

static struct gendisk *ramblock_disk;
static request_queue_t *ramblock_queue;

static int major;

static DEFINE_SPINLOCK(ramblock_lock);

#define RAMBLOCK_SIZE (1024*1024)
static unsigned char *ramblock_buf;

static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
	/* 容量=heads*cylinders*sectors*512 */
	geo->heads     = 2;
	geo->cylinders = 32;
	geo->sectors   = RAMBLOCK_SIZE/2/32/512;
	return 0;
}


static struct block_device_operations ramblock_fops = {
	.owner	= THIS_MODULE,
	.getgeo	= ramblock_getgeo,
};

static void do_ramblock_request(request_queue_t * q)
{
	static int r_cnt = 0;
	static int w_cnt = 0;
	struct request *req;
	
	//printk("do_ramblock_request %d\n", ++cnt);

	while ((req = elv_next_request(q)) != NULL) {
		/* 数据传输三要素: 源,目的,长度 */
		/* 源/目的: */
		unsigned long offset = req->sector * 512;

		/* 目的/源: */
		// req->buffer

		/* 长度: */		
		unsigned long len = req->current_nr_sectors * 512;

		if (rq_data_dir(req) == READ)
		{
			//printk("do_ramblock_request read %d\n", ++r_cnt);
			memcpy(req->buffer, ramblock_buf+offset, len);
		}
		else
		{
			//printk("do_ramblock_request write %d\n", ++w_cnt);
			memcpy(ramblock_buf+offset, req->buffer, len);
		}		
		
		end_request(req, 1);
	}
}

static int ramblock_init(void)
{
	/* 1. 分配一个gendisk结构体 */
	ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */

	/* 2. 设置 */
	/* 2.1 分配/设置队列: 提供读写能力 */
	ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
	ramblock_disk->queue = ramblock_queue;
	
	/* 2.2 设置其他属性: 比如容量 */
	major = register_blkdev(0, "ramblock");  /* cat /proc/devices */	
	ramblock_disk->major       = major;
	ramblock_disk->first_minor = 0;
	sprintf(ramblock_disk->disk_name, "ramblock");
	ramblock_disk->fops        = &ramblock_fops;
	set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);

	/* 3. 硬件相关操作 */
	ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);

	/* 4. 注册 */
	add_disk(ramblock_disk);

	return 0;
}

static void ramblock_exit(void)
{
	unregister_blkdev(major, "ramblock");
	del_gendisk(ramblock_disk);
	put_disk(ramblock_disk);
	blk_cleanup_queue(ramblock_queue);

	kfree(ramblock_buf);
}

module_init(ramblock_init);
module_exit(ramblock_exit);

MODULE_LICENSE("GPL");


  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值