• 块设备驱动实现--模拟一个块设备


    1、前言

            存储层在收到I/O请求后进行数据处理,再给上层应答,本文实现一个实际的块设备驱动。使用Linux5.4为基础,进行框架搭建和功能实现。

    2、ko模块与编译

            首先定义一个init和exit函数,去注册自己的驱动函数模块。

    1. #include
    2. #include
    3. static int __init init_myblk(void)
    4. {
    5. int ret;
    6. printk("start lsmod my blk_dev");
    7. my_disk_queue = blk_mq_init_sq_queue( &tag_set, &my_blk_mq_ops, 16, BLK_MQ_F_SHOULD_MERGE);
    8. if (!my_disk_queue) {
    9. ret = -ENOMEM;
    10. goto err_init_queue;
    11. }
    12. my_disk = alloc_disk(1);
    13. if (!my_disk) {
    14. ret = -ENOMEM;
    15. goto err_alloc_disk;
    16. }
    17. strcpy(my_disk->disk_name, MY_BLKDEV_DISKNAME);//设备名称
    18. my_disk->major = MY_BLKDEV_DEVICEMAJOR;//块设备的主设备号
    19. my_disk->first_minor = 0;
    20. my_disk->fops = &myblk_fops;//操作结构体
    21. my_disk->queue = my_disk_queue;//块设备驱动的请求队列
    22. set_capacity(my_disk, MY_BLKDEV_BYTES>>9);
    23. add_disk(my_disk);
    24. return 0;
    25. err_init_queue:
    26. printk("init blk_queue error");
    27. return ret;
    28. err_alloc_disk:
    29. printk("alloc blk error");
    30. return ret;
    31. }
    32. module_init(init_myblk);
    33. module_exit(exit_myblk);
    34. MODULE_LICENSE("GPL");

    这里还要在要Makefile里记得拉上自己的文件路径,在.config中加上编译的宏,并且写一个kconfig去配置这个宏。

    其中,my_disk_queue = blk_mq_init_sq_queue(&tag_set, &my_blk_mq_ops, 16, BLK_MQ_F_SHOULD_MERGE);    在5.4中块设备全面改成了blk_mq(多任务队列),所以相关的API都是和blk_mq有关系的,这个地方卡了好久好久,最终还是从z2ram.c中了解到的,后边io处理的队列也是参考z2ram.c来写的。

    所以接下来就是这个my_blk_queue_rq()的内容:

    1. static const struct blk_mq_ops my_blk_mq_ops = {
    2. .queue_rq = my_blk_queue_rq,
    3. };
    4. static blk_status_t my_blk_queue_rq(struct blk_mq_hw_ctx *hctx,
    5. const struct blk_mq_queue_data *bd)
    6. {
    7. struct request *req = bd->rq;
    8. unsigned long start = blk_rq_pos(req) << 9;
    9. unsigned long len = blk_rq_cur_bytes(req);
    10. void *buffer;
    11. blk_mq_start_request(req);
    12. if (start + len > MY_BLKDEV_BYTES) {
    13. printk(KERN_ERR MY_BLKDEV_DISKNAME
    14. ": bad request: block=%llu, count=%u\n",
    15. (unsigned long long)blk_rq_pos(req),
    16. blk_rq_cur_sectors(req));
    17. blk_mq_end_request(req, BLK_STS_OK);
    18. }
    19. spin_lock_irq(&myblk_lock);
    20. buffer = bio_data(req->bio);
    21. if (rq_data_dir(req) == READ)
    22. memcpy(buffer,
    23. my_blkdev_data + start,
    24. blk_rq_cur_sectors(req) << 9);
    25. else if(rq_data_dir(req) == WRITE)
    26. memcpy(buffer,
    27. my_blkdev_data + start,
    28. blk_rq_cur_sectors(req) << 9);
    29. spin_unlock_irq(&myblk_lock);
    30. blk_mq_end_request(req, BLK_STS_OK);
    31. return BLK_STS_OK;
    32. }

            实际上就是memcpy相关的字符串到块设备的存储空间内。然后各种编译。且要把blk_mq_init_sq_queue加入白名单(while list)。

    3、编译与使用

            在Android开发环境中,选择bootimage编译,修改Makefile和Kconfig后,会自动加载该模块。 使用lsmod可以发现这时候还没被使用,首先进行文件系统挂载:

    1. ir:/ # mkfs.ext4 dev/block/my_blkdev
    2. mke2fs 1.45.4 (23-Sep-2019)
    3. Creating filesystem with 16384 1k blocks and 4096 inodes
    4. Filesystem UUID: 200b5fcd-e9ea-4634-9c98-94a8bc58d1f2
    5. Superblock backups stored on blocks:
    6. 8193
    7. Allocating group tables: done
    8. Writing inode tables: done
    9. Creating journal (1024 blocks): done
    10. Writing superblocks and filesystem accounting information: done
    11. ir:/ # mount dev/block/my_blkdev mnt/tmp1/
    12. mount: 'dev/block/my_blkdev'->'mnt/tmp1/': I/O error

            但是,这里有个问题,将ko整机刷入后,会面临io调度导致的挂载失败问题, 需要进行修改。

    参考:[原创] 写一个块设备驱动 - 内核源码-Chinaunix

  • 相关阅读:
    使用Java对书籍照片进行字符分割
    linux centos 创建开机启动的spring boot 服务配置
    根据轮廓创建旋转框和椭圆
    JAVA--泛型
    云HIS-医院信息化的核心
    细聊.Net Core中IServiceScope的工作方式
    爬虫学习 | 01 Web Scraper的使用
    6. 【图的存储结构】邻接矩阵、邻接表 、十字链表 、邻接多重表
    Linux 编译安装中的 configure 命令
    02-Node.js模块化、模块作用域、module.exports
  • 原文地址:https://blog.csdn.net/qq_38473009/article/details/140402875