【嵌入式Linux】基于kernel6.1的块设备驱动(RAM Disk),示例代码和测试

📅 2026/7/23 10:44:45
【嵌入式Linux】基于kernel6.1的块设备驱动(RAM Disk),示例代码和测试
1.驱动结构梳理驱动使用BIOBLK_MQ框架BIO负责我们的具体驱动代码BLK_MQ负责分发处理BIO请求BLK_MQ是为了应对1.多核心分发2.合并相邻的块请求3.满足调度需求。其他具体的见大佬文章linux驱动移植-linux块设备驱动blk-mq - 大奥特曼打小怪兽 - 博客园2.源码结构zzl_ramdisk.c2.1.头文件2.2.宏定义2.3.定义、声明和变量2.4.open打开设备回调2.5.release关闭设备回调2.6.getgeo获取设备物理信息回调heads是磁头数量和盘片数量一般是对应关系cylinders代表一个盘片上磁道数量有多少圈sectors代表一个磁道有多少扇区硬盘电路会处理让软件上看上去所有磁道扇区数量一样2.7.fops装载2.4、2.5、2.6回调用于传入给上层虚文件系统VFS调用2.8.dev_handle_requestBLK_MQ请求处理回调轮询处理队列中的IO请求读取的话从设备中拷贝2.9.ramdisk_mq_ops装载2.8回调2.10.demo_init驱动初始化加载2.10.1.申请私人对象我们自定义的对象结构体的变量2.10.2.申请vfs的bufferRAM Disk中的RAM2.10.3.申请设备号2.10.4.申请并初始化BLK_MQ的目标信息回调、队列信息、节点选择2.10.5.使用目标信息和私人对象申请并初始化磁盘设备对象gendisk2.10.6.设置request_queue的扇区字节数参数2.10.7.添加磁盘设备2.11.demo_exit驱动卸载释放内存、删除设备3.源码#include linux/module.h #include linux/init.h #include linux/kernel.h #include linux/fs.h // 包含 file_operations 结构体 #include linux/uaccess.h // 包含 copy_to_user, copy_from_user #include linux/slab.h // 包含 kzalloc, kfree #include linux/blkdev.h #include linux/device.h #include linux/hdreg.h #include linux/blkdev.h #include linux/blk-mq.h // #include linux/genhd.h #include linux/vmalloc.h #include linux/spinlock.h #define DEVICE_NAME ZZL_RAMDISK #define RAMDISK_SIZE (16*1024*1024) // 16MiB #define RAMDISK_SECTOR_SIZE (512) #define RAMDISK_SECTOR_NUM (RAMDISK_SIZE/RAMDISK_SECTOR_SIZE) /* 设备私有数据指向模拟内存的指针 */ struct ramdisk_dev { unsigned char *buffer; }; static struct gendisk *ramdisk_gendisk; static struct ramdisk_dev *ramdisk_priv; static int ramdisk_major; static struct blk_mq_tag_set ramdisk_tag_set; static int dev_open(struct block_device *bdev, fmode_t mode) { printk(KERN_INFO 设备打开\n); return 0; } static void dev_release(struct gendisk *disk, fmode_t mode) { printk(KERN_INFO 设备关闭\n); } static int dev_getgeo(struct block_device *bdev, struct hd_geometry *geo) { geo-heads 4; geo-cylinders 32; geo-sectors RAMDISK_SECTOR_NUM/(geo-heads * geo-cylinders); return 0; } static const struct block_device_operations fops { .owner THIS_MODULE, .open dev_open, .getgeo dev_getgeo, .release dev_release, }; static blk_status_t dev_handle_request(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) { struct request *req bd-rq; struct ramdisk_dev *dev req-q-queuedata; struct bio *bio; struct bio_vec bvec; struct bvec_iter iter; unsigned long offset; size_t len; void *src, *dst; if (!dev || !dev-buffer) { printk(KERN_ALERT 私有数据或缓冲区无效\n); return BLK_STS_IOERR; } for (bio req-bio; bio; bio bio-bi_next) { offset bio-bi_iter.bi_sector SECTOR_SHIFT; if (offsetlen RAMDISK_SIZE) { pr_alert(bio 起始偏移越界: %lu\n, offset); return BLK_STS_IOERR; } bio_for_each_segment(bvec, bio, iter) { len bvec.bv_len; if (bio_data_dir(bio) READ) { src dev-buffer offset; dst kmap_local_page(bvec.bv_page) bvec.bv_offset; memcpy(dst, src, len); kunmap_local(dst); printk(KERN_INFO 读取 %zu - %zu\n, offset, offsetlen); } else { src kmap_local_page(bvec.bv_page) bvec.bv_offset; dst dev-buffer offset; memcpy(dst, src, len); kunmap_local(src); printk(KERN_INFO 写入 %zu - %zu\n, offset, offsetlen); } offset len; } } blk_mq_end_request(req, BLK_STS_OK); return BLK_STS_OK; } static const struct blk_mq_ops ramdisk_mq_ops { .queue_rq dev_handle_request, }; static int __init demo_init(void) { int ret 0; // 1. ramdisk_priv kzalloc(sizeof(*ramdisk_priv), GFP_KERNEL); if(IS_ERR(ramdisk_priv )) { printk(KERN_ALERT 申请Prive失败\n); return PTR_ERR(ramdisk_priv ); } printk(KERN_INFO 申请Prive 成功\n); ramdisk_priv-buffer vmalloc(RAMDISK_SIZE); if(IS_ERR(ramdisk_priv-buffer)) { printk(KERN_ALERT 申请Buffer失败\n); kfree(ramdisk_priv); return PTR_ERR(ramdisk_priv-buffer); } memset(ramdisk_priv-buffer, 0, RAMDISK_SIZE); printk(KERN_INFO 申请Buffer 成功\n); // 2. ramdisk_major register_blkdev(0, DEVICE_NAME); if(ramdisk_major 0) { printk(KERN_ALERT 分配驱动号失败\n); vfree(ramdisk_priv-buffer); kfree(ramdisk_priv); return ramdisk_major; } printk(KERN_INFO 驱动号%d\n, ramdisk_major); // . memset(ramdisk_tag_set, 0, sizeof(ramdisk_tag_set)); ramdisk_tag_set.ops ramdisk_mq_ops; ramdisk_tag_set.nr_hw_queues 1; ramdisk_tag_set.queue_depth 128; // 队列深度 ramdisk_tag_set.numa_node NUMA_NO_NODE; ramdisk_tag_set.flags BLK_MQ_F_SHOULD_MERGE; ramdisk_tag_set.cmd_size 0; ret blk_mq_alloc_tag_set(ramdisk_tag_set); if (ret) { printk(KERN_ALERT 分配 tag_set 失败\n); unregister_blkdev(ramdisk_major, DEVICE_NAME); vfree(ramdisk_priv-buffer); kfree(ramdisk_priv); return ret; } // 3. ramdisk_gendisk blk_mq_alloc_disk(ramdisk_tag_set, ramdisk_priv); if (IS_ERR(ramdisk_gendisk)) { // 错误处理注销设备号 printk(KERN_ALERT 申请GenDisk失败\n); vfree(ramdisk_priv-buffer); kfree(ramdisk_priv); unregister_blkdev(ramdisk_major, DEVICE_NAME); blk_mq_free_tag_set(ramdisk_tag_set); return PTR_ERR(ramdisk_gendisk); } printk(KERN_INFO 申请GenDisk 成功\n); // 3.1. ramdisk_gendisk-major ramdisk_major; ramdisk_gendisk-first_minor 0; ramdisk_gendisk-minors 1; strcpy(ramdisk_gendisk-disk_name, DEVICE_NAME); ramdisk_gendisk-fops fops; set_capacity(ramdisk_gendisk, RAMDISK_SECTOR_NUM); // 3.2. struct request_queue *req_q ramdisk_gendisk-queue; blk_queue_logical_block_size(req_q, RAMDISK_SECTOR_SIZE); blk_queue_physical_block_size(req_q, RAMDISK_SECTOR_SIZE); // 4. ret add_disk(ramdisk_gendisk); if(ret) { printk(KERN_ALERT 添加Disk失败\n); vfree(ramdisk_priv-buffer); kfree(ramdisk_priv); unregister_blkdev(ramdisk_major, DEVICE_NAME); blk_mq_free_tag_set(ramdisk_tag_set); return ret; } printk(KERN_INFO 驱动完成注册\n); return ret; } static void __exit demo_exit(void) { del_gendisk(ramdisk_gendisk); put_disk(ramdisk_gendisk); unregister_blkdev(ramdisk_major, DEVICE_NAME); vfree(ramdisk_priv-buffer); kfree(ramdisk_priv); blk_mq_free_tag_set(ramdisk_tag_set); printk(KERN_INFO 驱动卸载\n); } module_init(demo_init); module_exit(demo_exit); MODULE_AUTHOR([ZZL]); MODULE_DESCRIPTION(Testing block (Ram Disk) driver); MODULE_LICENSE(GPL v2);4.Makefileobj-m zzl_ramdisk.o all: make -C /lib/modules/$(shell uname -r)/build M$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M$(PWD) clean5.测试步骤5.1.编译安装驱动编译为.ko文件然后用insmod安装.ko文件make sudo insmod zzl_ramdisk.ko5.2.创建文件夹、挂载设备sudo mkdir -p /mnt/ramdisk sudo mount /dev/ZZL_RAMDISK /mnt/ramdisk5.3.创建文件查看磁盘状态echo Hello RAM Disk | sudo tee /mnt/ramdisk/test.txt dd if/dev/urandom of/mnt/ramdisk/random_file.bin bs8M count1 statusprogress ls -l /mnt/ramdisk/ df -h