当前位置: 首页 > news >正文

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

1.驱动结构梳理

驱动使用BIO+BLK_MQ框架,BIO负责我们的具体驱动代码,BLK_MQ负责分发处理BIO请求(BLK_MQ是为了应对:1.多核心分发;2.合并相邻的块请求;3.满足调度需求)。

其他具体的见大佬文章:linux驱动移植-linux块设备驱动blk-mq - 大奥特曼打小怪兽 - 博客园

2.源码结构zzl_ramdisk.c

2.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_request,BLK_MQ请求处理回调

轮询处理队列中的IO请求,读取的话,从设备中拷贝

2.9.ramdisk_mq_ops装载2.8回调

2.10.demo_init驱动初始化(加载)

2.10.1.申请私人对象(我们自定义的对象)结构体的变量

2.10.2.申请vfs的buffer(RAM Disk中的RAM)

2.10.3.申请设备号

2.10.4.申请并初始化BLK_MQ的目标信息(回调、队列信息、节点选择)

2.10.5.使用目标信息和私人对象,申请并初始化磁盘设备对象gendisk

2.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 (offset+len >= 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, offset+len); } 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, offset+len); } 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.Makefile

obj-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) clean

5.测试步骤

5.1.编译安装驱动

编译为.ko文件,然后用insmod安装.ko文件

make sudo insmod zzl_ramdisk.ko

5.2.创建文件夹、挂载设备

sudo mkdir -p /mnt/ramdisk sudo mount /dev/ZZL_RAMDISK /mnt/ramdisk

5.3.创建文件,查看磁盘状态

echo "Hello RAM Disk" | sudo tee /mnt/ramdisk/test.txt dd if=/dev/urandom of=/mnt/ramdisk/random_file.bin bs=8M count=1 status=progress ls -l /mnt/ramdisk/ df -h

http://www.jsqmd.com/news/1247250/

相关文章:

  • 网页版Excel核心功能与高效办公实践指南
  • 【AI搜索数据分析报告终极指南】:2024年9大实战陷阱、3类高危误判及5步精准归因法
  • 企业微信API开发教程:客户添加接口实现与流程解析
  • 破解抗干扰难题:ICS三重抗干扰方法论如何打造稳定的机加工专用电永磁吸盘? - 全域品牌推荐
  • SD图生图模式:原理、参数配置与实战应用
  • AI自助服务工具的核心能力与行业应用解析
  • 老牌聚脲美缝剂品牌盘点:谁更值得选 - 资讯报道
  • UE5 AssetManager:大型项目资源加载与内存管理实战指南
  • 智能体(Agent)开发指南:从入门到商业化实践
  • 技术博文写作指南:如何基于明确需求策划有价值的AI开发内容
  • LLM全栈开发核心名词与技术实践指南
  • 第十章WSaiOS 感知记忆引擎实现
  • AI论文写作工具对比:千笔与文途AI的核心功能与应用
  • 2026年江苏淘宝代运营公司选购 多维度实用参考指南 - 热点速览
  • 无线充电技术
  • 2026观音桥离婚财产分割涉及股权房产该找哪家律所 - 热点速览
  • Dev-C++图形化五子棋开发:从EasyX界面到AI算法实战
  • BQ40Z50-R5 SBS命令实战:从阻抗跟踪到电芯均衡的深度解析
  • 2026年最新教程:图片DPI怎么查看和修改 亲测免费方法 - 图片处理研究员
  • Python实现可再生能源与电动汽车协同调度优化
  • TPS61199多路LED背光驱动芯片:从电流模式控制到PCB布局的实战设计指南
  • 游戏前端渲染优化复盘:Canvas 与 WebGL 的性能调优
  • CS2七月更新后常见问题与优化解决方案
  • 2026东莞黄金回收避坑攻略,卖黄金怎样避开虚高报价恶意扣费? - 企业家观察员
  • 佳能LBP621cw彩色激光打印机评测与选购指南
  • ReClassEx与ReClass.NET深度对比:游戏逆向工具选型指南
  • TM4C129DNCPDT看门狗与ADC协同设计:嵌入式系统可靠性的核心实践
  • Hermes Agent:具备自我进化能力的AI智能体系统解析
  • 财务日常工作_20260723
  • 从一个普通程序员的角度,聊聊当前环境下,是否适合做编程