嵌入式Linux RAMDisk驱动开发

嵌入式Linux RAMDisk驱动开发

1. 概述

本章将详细介绍嵌入式Linux系统中RAMDisk块设备驱动的开发。RAMDisk是一种基于内存的块设备,它将系统内存模拟成块设备使用。本章以i.MX6ULL开发板为例,详细分析RAMDisk驱动的实现原理和代码结构。

2. RAMDisk驱动原理

2.1 块设备基础

在Linux系统中,设备分为字符设备、块设备和网络设备三大类。块设备的特点是:

  • 以固定大小的数据块为单位进行读写
  • 支持随机访问
  • 数据传输通过请求队列(request queue)进行管理
  • 使用缓冲区缓存机制提高性能

块设备驱动的核心数据结构包括:

  • struct gendisk: 通用磁盘结构,描述磁盘的基本信息
  • struct request_queue: 请求队列,管理I/O请求
  • struct block_device_operations: 块设备操作函数集

2.2 RAMDisk工作原理

RAMDisk驱动的工作原理是将一段内存区域模拟成块设备。当上层应用对RAMDisk进行读写操作时,驱动程序将数据直接从内存中读取或写入内存,从而实现类似磁盘的存储功能。

主要特点:

  • 访问速度快:直接操作内存,无需物理磁盘I/O
  • 断电数据丢失:内存中的数据在系统断电后会丢失
  • 容量有限:受限于系统可用内存大小
  • 可重复使用:可以像普通磁盘一样格式化、分区和挂载

3. 驱动代码分析

3.1 头文件包含

c 复制代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/input/mt.h>
#include <linux/delay.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>

这些头文件提供了驱动开发所需的基本功能:

  • linux/module.h: 模块相关定义
  • linux/kernel.h: 内核常用宏和函数
  • linux/init.h: 初始化相关宏
  • linux/fs.h: 文件系统相关定义
  • linux/slab.h: 内存分配函数
  • linux/blkdev.h: 块设备相关定义
  • linux/hdreg.h: 硬盘寄存器相关定义

3.2 宏定义

c 复制代码
#define RAMDISK_SIZE (2 * 1024 * 1024)
#define RMADISK_NAME "ramdisk"
#define RAMDISK_MINOR 3
  • RAMDISK_SIZE: 定义RAMDisk的大小为2MB
  • RMADISK_NAME: 设备名称
  • RAMDISK_MINOR: 次设备号数量

3.3 设备结构体

c 复制代码
struct ramdisk_dev
{
    int major;
    u8 *ramdiskbuf;
    struct gendisk *gendisk;
    struct request_queue *queue;
    spinlock_t lock;
};
struct ramdisk_dev ramdisk;

ramdisk_dev结构体用于管理RAMDisk设备的各个组件:

  • major: 主设备号
  • ramdiskbuf: 指向分配的内存缓冲区
  • gendisk: 通用磁盘结构指针
  • queue: 请求队列指针
  • lock: 自旋锁,用于同步访问

3.4 数据传输函数

c 复制代码
static void ramdisk_transfer(struct request *req)
{
    unsigned long start = blk_rq_pos(req) << 9;
    unsigned long len = blk_rq_cur_bytes(req);

    void *buffer = bio_data(req->bio);
    if (rq_data_dir(req) == READ)
        memcpy(buffer, ramdisk.ramdiskbuf + start, len);
    else
        memcpy(ramdisk.ramdiskbuf + start, buffer, len);
}

ramdisk_transfer函数负责实际的数据传输:

  • blk_rq_pos(req): 获取请求的起始扇区号
  • << 9: 将扇区号转换为字节偏移(512字节/扇区)
  • blk_rq_cur_bytes(req): 获取当前请求的数据长度
  • bio_data(req->bio): 获取数据缓冲区地址
  • 根据请求方向(读/写)执行相应的内存拷贝操作

3.5 请求处理函数

c 复制代码
static void ramdisk_request_fn(struct request_queue *q)
{
    int err = 0;
    struct request *req;
    req = blk_fetch_request(q);
    while (req)
    {
        ramdisk_transfer(req);
        if (!__blk_end_request_cur(req, err))
        {
            req = blk_fetch_request(q);
        }
    }
}

ramdisk_request_fn是请求队列的处理函数:

  • 从请求队列中获取一个请求
  • 调用ramdisk_transfer进行数据传输
  • 使用__blk_end_request_cur结束当前请求
  • 如果还有剩余请求,继续处理下一个

3.6 块设备操作函数

c 复制代码
static int ramdisk_open(struct block_device *bdev, fmode_t mode)
{
    return 0;
}

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

static int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo)
{
    geo->heads = 2;
    geo->cylinders = 32;
    geo->sectors = RAMDISK_SIZE / (2 * 32 * 512);
    return 0;
}

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

块设备操作函数集定义了设备的基本操作:

  • open: 设备打开函数,返回0表示成功
  • release: 设备释放函数,空实现
  • getgeo: 获取设备几何信息,用于兼容传统磁盘操作

3.7 模块初始化函数

c 复制代码
static int __init ramdisk_init(void)
{
    int ret = 0;
    ramdisk.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL);
    if (ramdisk.ramdiskbuf == NULL)
    {
        ret = -EINVAL;
        goto fail_kzalloc;
    }

    ramdisk.major = register_blkdev(0, RMADISK_NAME);
    if (ramdisk.major < 0)
    {
        ret = -EINVAL;
        goto fail_register_blkdev;
    }

    printk("Driver: ramdisk major is %#x\r\n", ramdisk.major);

    spin_lock_init(&ramdisk.lock);
    ramdisk.queue = blk_init_queue(ramdisk_request_fn, &ramdisk.lock);
    if (ramdisk.queue == NULL)
    {
        ret = -EINVAL;
        goto fail_blk_init_queue;
    }

    ramdisk.gendisk = alloc_disk(RAMDISK_MINOR);
    if (ramdisk.gendisk == NULL)
    {
        ret = -EINVAL;
        goto fail_alloc_disk;
    }

    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);
    add_disk(ramdisk.gendisk);

    return 0;

fail_alloc_disk:
    blk_cleanup_queue(ramdisk.queue);
    printk("Driver: fail_alloc_disk\r\n");
fail_blk_init_queue:
    unregister_blkdev(ramdisk.major, RMADISK_NAME);
    printk("Driver: fail_blk_init_queue\r\n");
fail_register_blkdev:
    kfree(ramdisk.ramdiskbuf);
    printk("Driver: fail_register_blkdev\r\n");
fail_kzalloc:
    printk("Driver: fail_kzalloc\r\n");
    return ret;
}

ramdisk_init函数完成了驱动的初始化工作:

  1. 分配内存缓冲区
  2. 注册块设备,获取主设备号
  3. 初始化自旋锁
  4. 创建请求队列
  5. 分配通用磁盘结构
  6. 配置磁盘参数
  7. 添加磁盘到系统

错误处理采用goto模式,确保资源正确释放。

3.8 模块退出函数

c 复制代码
static void __exit ramdisk_exit(void)
{
    del_gendisk(ramdisk.gendisk);
    blk_cleanup_queue(ramdisk.queue);
    unregister_blkdev(ramdisk.major, RMADISK_NAME);
    kfree(ramdisk.ramdiskbuf);
}

ramdisk_exit函数负责清理资源:

  • 从系统中删除磁盘
  • 清理请求队列
  • 注销块设备
  • 释放内存缓冲区

4. 编译配置

4.1 Makefile分析

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_modules

kernel_modules:
	$(MAKE) -C $(KERNERDIR) M=$(CURRENTDIR) modules

clean:
	$(MAKE) -C $(KERNERDIR) M=$(CURRENTDIR) clean

Makefile关键点:

  • KERNERDIR: 内核源码路径
  • CURRENTDIR: 当前目录路径
  • obj-m: 指定生成ramdisk.ko模块
  • 编译命令使用内核构建系统进行模块编译

5. 驱动加载与测试

5.1 编译驱动

bash 复制代码
make

5.2 加载驱动

bash 复制代码
insmod ramdisk.ko

5.3 查看设备信息

bash 复制代码
dmesg | tail

应该能看到类似输出:

复制代码
Driver: ramdisk major is 0xXX

5.4 格式化和挂载

bash 复制代码
# 格式化为ext4文件系统
mkfs.ext4 /dev/ramdisk

# 创建挂载点
mkdir /mnt/ramdisk

# 挂载设备
mount /dev/ramdisk /mnt/ramdisk

# 测试读写
echo "Hello RAMDisk" > /mnt/ramdisk/test.txt
cat /mnt/ramdisk/test.txt

5.5 卸载和卸载驱动

bash 复制代码
# 卸载文件系统
umount /mnt/ramdisk

# 卸载驱动模块
rmmod ramdisk

6. 性能特点与应用场景

6.1 性能特点

  • 优点:

    • 极高的读写速度
    • 低延迟
    • 无机械磨损
    • 支持频繁的读写操作
  • 缺点:

    • 断电数据丢失
    • 容量受限于内存
    • 占用系统内存资源

6.2 应用场景

  • 临时文件存储
  • 高频读写缓存
  • 系统启动临时空间
  • 嵌入式系统中的快速存储需求
  • 测试和调试环境

7. 设备树配置

虽然RAMDisk是纯软件实现的虚拟设备,不需要硬件资源,但可以从设备树中获取一些系统信息。在imx6ull-alientek-emmc.dts中,我们可以看到系统内存配置:

dts 复制代码
memory {
    reg = <0x80000000 0x20000000>;
};

这表示系统内存从0x80000000开始,大小为512MB。RAMDisk分配的内存来自这片区域。

源码仓库位置: https://gitee.com/dream-cometrue/linux_driver_imx6ull

相关推荐
FIT2CLOUD飞致云2 小时前
功能持续优化,应用商店新增CRM分类,1Panel v2.0.10版本正式发布
运维·开源
RUNNING123!3 小时前
华为eNSP防火墙综合网络结构训练.docx
运维·网络·华为·ssh
_flierx3 小时前
【Linux】进程信号
linux·运维·服务器
贾saisai3 小时前
LINUX驱动篇(二)驱动开发
linux·驱动开发
半梦半醒*3 小时前
ansible判断
linux·运维·centos·ansible·运维开发
2501_913981783 小时前
串口服务器技术详解:2025年行业标准与应用指南
运维·服务器·串口服务器
姓刘的哦3 小时前
Linux驱动开发学习笔记
linux·驱动开发·笔记·学习
礼拜天没时间.3 小时前
Tomcat 企业级运维实战系列(三):Tomcat 配置解析与集群化部署
运维·centos·tomcat·firefox
idward3074 小时前
Android的USB通信 (AOA Android开放配件协议)
android·linux