linux-块设备简单总结


前言

@和原子哥一起学习Linux

开发环境:I.MX6Ull开发板

参考书籍:
《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6.pdf》
《linux设备驱动开发详解-基于最新的linux4.0内核》

个人学习笔记,欢迎讨论

一、什么是块设备?

块设备驱动其实就是存储设备驱动,块设备只能以块为单位进行读写访问。

二、使用梳理

linux 内核使用 gendisk 来描述一个磁盘设备, 使用request_queue、 request 和 bio去读写数据,每个磁盘(gendisk)都要分配一个 request_queue。

内核将对块设备的读写都发送到请求队列 request_queue 中, request_queue 中是大量的request(请求结构体),而 request 又包含了 bio, bio 保存了读写相关数据。

在这里插入图片描述
在这里插入图片描述
对于机械硬盘而言,将那些杂乱的访问按照一定的顺序进行排列可以有效提高磁盘性能, linux 里面针对不同的存储设备实现了不同的 I/O 调度算法。
1)机械硬盘这样的存储设备,需要 I/O 调度器来优化数据读写过程,需要使用请求队列读写设备
2)对于 EMMC、 SD 卡这样的非机械设备,可以进行完全随机访问,所以就不需要复杂的 I/O 调度器,不需要请求队列

1.使用请求队列

1.1 初始化

省略错误 。。。

	//申请内存
	ramdisk.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL);
     。。。
  //初始化自旋锁,后面用
	spin_lock_init(&ramdisk.lock);

	//注册块设备
	ramdisk.major = register_blkdev(0, RAMDISKNAME);
     。。。

	//申请设备gendisk,申请3个分区
    ramdisk.gendisk = alloc_disk(RAMDISK_MINOR);
     。。。

	//分配初始化请求队列
    ramdisk.queue = blk_init_queue(do_ramdisk_request, &ramdisk.lock);
     。。。

	//注册disk
    ramdisk.gendisk->major = ramdisk.major;
    ramdisk.gendisk->first_minor = 0; //起始设备号
    ramdisk.gendisk->fops = &ramdisk_fops;
    ramdisk.gendisk->queue = ramdisk.queue;
    sprintf(ramdisk.gendisk->disk_name, RAMDISKNAME);
    //使用 set_capacity 函数设置本块设备容量大小
	set_capacity(ramdisk.gendisk, RAMDISK_SIZE/512);
 	//将 gendisk 添加到内核中,也就是向内核添加一个磁盘设备。
    add_disk(ramdisk.gendisk);
    return 0;

1.2 队列请求处理函数

static void ramdisk_transfer(struct request *req)
{
	unsigned long start = blk_rq_pos(req) << 9; //扇区地址转字节地址
	unsigned long len  = blk_rq_cur_bytes(req);
	//使用 bio_data 函数获取请求中 bio 保存的数据
	void *buffer = bio_data(req->bio);
	if (rq_data_dir(req) == READ)
		memcpy(buffer, (char *)ramdisk.ramdiskbuf + start, len);
	else
		memcpy((char *)ramdisk.ramdiskbuf + start, buffer, len);
}

static void do_ramdisk_request(struct request_queue *q)
{
	struct request *req;
	int err = 0;
	//循环处理队列
	req = blk_fetch_request(q);
	while (req != NULL) {
		ramdisk_transfer(req);
		//* 判断是否为最后一个请求,如果不是的话就获取下一个请求
		if (!__blk_end_request_cur(req, err))
			req = blk_fetch_request(q);
	}
}

2.不使用请求队列

2.1 初始化

省略错误 。。。

	//申请内存
	ramdisk.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL);
   。。。
   
	//注册设备
	ramdisk.major = register_blkdev(0, RAMDISKNAME);
   。。。

	//申请设备gendisk,申请3个分区
    ramdisk.gendisk = alloc_disk(RAMDISK_MINOR);
   。。。

//分配初始化请求队列
ramdisk.queue = blk_alloc_queue(GFP_KERNEL);

。。。
//设置制造请求
blk_queue_make_request(ramdisk.queue, ramdisk_make_request_fn);

	//注册disk
    ramdisk.gendisk->major = ramdisk.major;
    ramdisk.gendisk->first_minor = 0; //起始设备号
    ramdisk.gendisk->fops = &ramdisk_fops;
    ramdisk.gendisk->queue = ramdisk.queue;
    sprintf(ramdisk.gendisk->disk_name, RAMDISKNAME);
    //使用 set_capacity 函数设置本块设备容量大小
	set_capacity(ramdisk.gendisk, RAMDISK_SIZE/512);
 	//将 gendisk 添加到内核中,也就是向内核添加一个磁盘设备。
    add_disk(ramdisk.gendisk);
    return 0;

2.2 制造请求函数

static void ramdisk_make_request_fn(struct request_queue *q, struct bio *bio)
{
	int offset;
	struct bio_vec bvec;
	struct bvec_iter iter;
    unsigned long len = 0;

	offset = bio->bi_iter.bi_sector << 9; //获取设备偏移地址
	bio_for_each_segment(bvec, bio, iter) {
		char *ptr = page_address(bvec.bv_page) + bvec.bv_offset;
		len = bvec.bv_len;

		if (bio_data_dir(bio) == READ)
			memcpy(ptr, (char *)ramdisk.ramdiskbuf + offset, len);
		else if(bio_data_dir(bio) == WRITE) /* 写数据 */
			memcpy((char *)ramdisk.ramdiskbuf + offset, ptr, len);
		offset += len;	
	}

	set_bit(BIO_UPTODATE, &bio->bi_flags);
	bio_endio(bio, 0);
	return;
}

运行验证

加载模块
fdisk -l //查看磁盘信息
mkfs.vfat /dev/ramdisk//格式化/dev/ramdisk
mount /dev/ramdisk /tmp//挂载后就可以访问了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值