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

基于i.MX6ULL的RAM Disk驱动开发

基于i.MX6ULL的RAM Disk驱动开发

本文详细分析基于i.MX6ULL平台的RAM Disk块设备驱动实现,结合代码和理论知识,深入讲解Linux块设备驱动的核心概念和实现方法。

1. 源码仓库

本文档所涉及的源代码位于:https://gitee.com/dream-cometrue/linux_driver_imx6ull

2. 驱动概述

本驱动实现了一个基于内存的虚拟块设备(RAM Disk),它将系统内存的一部分模拟成块设备,使得上层应用程序可以像操作物理磁盘一样对其进行读写操作。该驱动位于26_ramdisk_makerequest目录下,主要包含两个文件:

  • ramdisk.c:核心驱动代码
  • Makefile:编译脚本

3. 理论基础

3.1 Linux块设备驱动架构

Linux块设备驱动的核心组件包括:

  • struct gendisk:通用磁盘结构体,代表一个块设备
  • struct request_queue:请求队列,管理I/O请求
  • struct block_device_operations:块设备操作函数集
  • BIO(Block I/O)层:处理块I/O请求的基本单位

3.2 请求处理机制

块设备驱动有两种主要的请求处理方式:

  1. Request-based:传统的请求队列处理方式,使用struct request结构
  2. Make-request-based:直接处理BIO请求的方式,更高效

本驱动采用Make-request-based方式,通过blk_queue_make_request()函数设置请求处理函数。

4. 核心数据结构

struct ramdisk_dev {int major;                    // 主设备号u8 *ramdiskbuf;               // RAM磁盘缓冲区struct gendisk *gendisk;      // 通用磁盘结构struct request_queue *queue;  // 请求队列spinlock_t lock;              // 自旋锁
};

该结构体定义了RAM Disk设备的核心数据成员,使用全局变量ramdisk实例化。

5. 驱动初始化

5.1 模块入口函数

static int __init ramdisk_init(void)

驱动的初始化函数,执行以下关键步骤:

5.1.1 内存分配
ramdisk.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL);

使用kzalloc()分配2MB的连续内存空间作为RAM磁盘的存储区域,GFP_KERNEL标志表示在常规内核内存区分配。

5.1.2 注册块设备
ramdisk.major = register_blkdev(0, RMADISK_NAME);

动态注册块设备,主设备号由系统分配(传入0)。

5.1.3 初始化自旋锁
spin_lock_init(&ramdisk.lock);

初始化自旋锁,用于多处理器环境下的同步。

5.1.4 分配请求队列
ramdisk.queue = blk_alloc_queue(GFP_KERNEL);

使用现代APIblk_alloc_queue()分配请求队列,替代了已废弃的blk_init_queue()

5.1.5 分配通用磁盘
ramdisk.gendisk = alloc_disk(RAMDISK_MINOR);

分配gendisk结构体,RAMDISK_MINOR定义了次设备号的数量。

5.1.6 设置请求处理函数
blk_queue_make_request(ramdisk.queue, ramdisk_make_request);

ramdisk_make_request函数注册为请求处理函数,这是make-request模式的核心。

5.1.7 配置gendisk
ramdisk.gendisk->private_data = &ramdisk;
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, "ramdisk");
set_capacity(ramdisk.gendisk, RAMDISK_SIZE / 512);

配置gendisk结构体的各项参数,包括私有数据指针、主次设备号、操作函数集、请求队列、设备名称和容量(以512字节扇区为单位)。

5.1.8 注册磁盘
add_disk(ramdisk.gendisk);

将配置好的gendisk添加到系统中,此时设备对用户空间可见。

6. 请求处理函数

6.1 make_request函数

static void ramdisk_make_request(struct request_queue *queue, struct bio *bio)

这是驱动的核心函数,直接处理BIO请求,相比传统的request模式更高效。

6.1.1 计算偏移量
offset = bio->bi_iter.bi_sector << 9;

将起始扇区号转换为字节偏移量(左移9位相当于乘以512)。

6.1.2 遍历BIO向量
bio_for_each_segment(bvec, bio, iter)

使用bio_for_each_segment宏遍历BIO的所有段(segment),因为一个BIO可能包含非连续的内存页。

6.1.3 数据传输
char *ptr = page_address(bvec.bv_page) + bvec.bv_offset;
len = bvec.bv_len;if (bio_data_dir(bio) == READ)memcpy(ptr, ramdisk.ramdiskbuf + offset, len);
elsememcpy(ramdisk.ramdiskbuf + offset, ptr, len);

根据I/O方向(读或写),使用memcpy在RAM磁盘缓冲区和用户缓冲区之间复制数据。

6.1.4 完成BIO请求
set_bit(BIO_UPTODATE, &bio->bi_flags);
bio_endio(bio, 0);

标记BIO为"up-to-date"状态,并调用bio_endio()完成请求,通知上层。

7. 块设备操作函数

static const struct block_device_operations ramdisk_fops = {.owner = THIS_MODULE,.open = ramdisk_open,.release = ramdisk_release,.getgeo = ramdisk_getgeo,
};

7.1 打开函数

static int ramdisk_open(struct block_device *bdev, fmode_t mode)

简单的存根函数,始终返回成功,因为RAM磁盘无需复杂的打开操作。

7.2 释放函数

static void ramdisk_release(struct gendisk *disk, fmode_t mode) {}

空的释放函数,与open函数对应。

7.3 获取几何信息

static int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo)

提供磁盘的几何信息,用于兼容传统工具。虽然现代Linux主要使用LBA寻址,但此函数仍有必要实现。

8. 驱动卸载

8.1 模块退出函数

static void __exit ramdisk_exit(void)

执行以下清理操作:

  • del_gendisk():从系统中删除磁盘
  • blk_cleanup_queue():清理请求队列
  • unregister_blkdev():注销块设备
  • kfree():释放RAM磁盘缓冲区内存

9. Makefile分析

KERNERDIR := /home/ubuntu2004/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENTDIR := $(shell pwd)obj-m := ramdisk.o
build : kernel_moduleskernel_modules:$(MAKE) -C $(KERNERDIR) M=$(CURRENTDIR) modulesclean:$(MAKE) -C $(KERNERDIR) M=$(CURRENTDIR) clean

9.1 变量定义

  • KERNERDIR:指向内核源码树根目录
  • CURRENTDIR:当前工作目录
  • obj-m:指定要编译为模块的目标文件

9.2 编译规则

使用内核构建系统进行编译,M=$(CURRENTDIR)参数告诉内核构建系统在指定目录下查找模块源码。

11. 编译与测试

11.1 编译驱动

make

26_ramdisk_makerequest目录下执行make命令,生成ramdisk.ko模块文件。

11.2 加载驱动

insmod ramdisk.ko

加载模块后,系统会分配主设备号,并创建/dev/ramdisk设备节点。

11.3 创建文件系统

mkfs.ext4 /dev/ramdisk

在RAM磁盘上创建ext4文件系统。

11.4 挂载使用

mkdir /mnt/ramdisk
mount /dev/ramdisk /mnt/ramdisk

创建挂载点并挂载RAM磁盘。

11.5 测试性能

# 写测试
dd if=/dev/zero of=/mnt/ramdisk/testfile bs=1M count=100
# 读测试
dd if=/mnt/ramdisk/testfile of=/dev/null bs=1M

由于RAM磁盘基于内存,其读写速度远超物理磁盘。

http://www.dtcms.com/a/360252.html

相关文章:

  • Linux目录和命令介绍
  • 如何建立奢侈品牌的数字资产安全管控体系?
  • leetcode 371 两个整数之和
  • 智能数据建设与治理 Dataphin-数仓分层
  • 基于AI的大模型在S2B2C商城小程序中的应用与定价策略自我评估
  • “机器人管家“离我们还有多远?
  • 7.3 el-menu
  • 【C2000】C2000的硬件设计指导与几点意见
  • 官方 API 与网络爬虫的技术特性对比及选型分析
  • shell编程基础入门-2
  • Overleaf中文显示
  • 把 `QVector<QPointF>` 写入文件:三种风格、三段独立可编译的完整代码
  • APB协议​​ 构建一个完整的 ​​UVM验证VIP Agent介绍类的要素
  • 《隐性质量:决定软件生命周期的看不见的竞争力》
  • 【Linux】创建线程
  • flutter工程
  • 包的相对导入
  • Android开发简介
  • Redis常见数据类型及应用场景
  • Pytest 插件介绍和开发
  • 极客时间AI 全栈开发实战营毕业总结(2025年8月31日)
  • NCCL-TEST ib集群测试UCX代替方案
  • mit6.031软件构造 笔记 Testing
  • ROI、 binning、下采样功能区别
  • windows编译minicap.so文件
  • 由题构造 嵌入汇编(汇编)
  • NAS Docker 安装N8N
  • 计算机视觉与深度学习 | 双目立体特征提取与匹配算法综述——理论基础、OpenCV实践与MATLAB实现指南
  • 猛犸Lark max 无线麦克风录音爆音问题的解决
  • 【STC库函数】使用芯片自带的EEPROM来保存掉电不丢失的数据