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

基于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.xdnf.cn/news/19354.html

相关文章:

  • 【开题答辩全过程】以 付费自习室系统小程序为例,包含答辩的问题和答案
  • 【编号186】中国劳动统计年鉴(1991-2023)
  • kafka服务端架构总览
  • 【C++】显示与隐式加载dll的使用与区别
  • 技术框架之RPC
  • 【系统分析师】高分论文:论信息系统的安全与保密设计
  • c++ 线程局部存储(Thread-Local Storage,TLS)
  • 当服务器报警响起:CPU、内存、磁盘使用率飙升的诊断与处置
  • 基于单片机智能鞋柜/智能鞋橱/智能鞋盒
  • C++项目的Makefile案例解析
  • Python基础之元组列表集合字典
  • Python与Rust语法对比详解:从入门到精通
  • C++顺序嵌套回调函数
  • 飞牛NAS上部署Markdown文稿编辑器,阅读.md文件同时还可以跨平台访问!
  • Qt精华版打包教程,支持windows和Linux,每种平台支持2种方案
  • 电感值过大过小会影响什么
  • AI基础学习周报十一
  • 艾体宝案例 | 数据驱动破局:DOMO 如何重塑宠物零售门店的生存法则
  • HarmonyOS 应用开发:基于API 12+的现代化实践
  • C++从入门到实战(二十)详细讲解C++List的使用及模拟实现
  • Ubuntu安装NVIDIA显卡驱动
  • #Datawhale 组队学习#8月-工作流自动化n8n入门-3
  • LabVIEW 瀑布图与游标操作
  • 分布式事务相关
  • [软考中级]嵌入式系统设计师—核心知识点速记
  • 分布式相关
  • 【iOS】MVC架构
  • 自制扫地机器人(一)20 元级机械自动避障扫地机器人——东方仙盟
  • 晶晨线刷工具下载及易错点说明:生成工作流程XML失败
  • Trie树(静态数组实现)