深入了解 Linux 中的 MTD 设备:/dev/mtd* 与 /dev/mtdblock*

目录

  • 前言
  • 一、什么是MTD子系统?
  • 二、 `/dev/mtd*` 设备文件
      • 用途
      • 注意事项
  • 三、`/dev/mtdblock*` 设备文件
      • 用途
      • 注意事项
  • 三、这两种设备文件的关系
  • 四、关norflash的一些小知识

前言

  在嵌入式Linux系统的世界里,非易失性存储技术扮演着至关重要的角色。MTD(Memory Technology Device)子系统是Linux内核的一个组成部分,它为各种类型的闪存和EEPROM设备提供了一个统一的接口。本文将深入探讨Linux系统中的两种MTD设备文件:/dev/mtd*/dev/mtdblock*,它们的用途、区别以及如何在实际场景中应用这些知识。
在这里插入图片描述

一、什么是MTD子系统?

  MTD(Memory Technology Device)子系统是 Linux 内核中的一个子系统,用于管理非易失性存储器设备,如闪存芯片(NAND、NOR 等)。MTD 子系统提供了一组通用的接口和驱动程序,使得 Linux 能够方便地访问和操作这些存储设备。

  MTD 子系统的主要功能包括:

  1. 设备抽象和管理:MTD 子系统提供了一个设备抽象层,允许系统对不同类型的非易失性存储设备进行统一的管理。它提供了一种通用的方式来表示和操作这些设备,无论它们是基于 NAND、NOR 还是其他技术。

  2. 设备擦除和编程:MTD 子系统允许 Linux 内核对支持擦除和编程操作的存储设备执行这些操作。这对于闪存等设备是至关重要的,因为它们通常需要在写入新数据之前先擦除存储单元。

  3. 分区支持:MTD 子系统允许将存储设备划分为多个逻辑分区,每个分区可以独立管理。这使得在同一个物理设备上实现多个逻辑存储空间成为可能,可以用于实现文件系统、内核映像等。

  4. 驱动程序支持:MTD 子系统提供了一组通用的驱动程序接口,使得开发者可以编写适配不同类型存储设备的驱动程序,并将其与 MTD 子系统集成起来。

总的来说,MTD 子系统为 Linux 内核提供了与非易失性存储设备交互的标准接口,使得开发者可以更方便地实现对这些设备的管理、操作和访问。

二、 /dev/mtd* 设备文件

  /dev/mtd*设备文件是MTD子系统的核心组成部分。这些文件代表了系统中的MTD设备,通常用于访问小块的、页对齐的内存区域。每个/dev/mtd*设备文件都有一个数字后缀,例如/dev/mtd0/dev/mtd1等,这些数字代表了设备在系统中的索引。

用途

  • 系统更新/dev/mtd*设备常用于更新系统的引导加载程序(bootloader)和内核。在嵌入式系统中,这些组件通常存储在闪存设备上,需要通过MTD子系统进行更新。
  • 数据存储:某些系统可能会使用MTD设备来存储关键的数据,如U-Boot环境变量或其他系统配置信息。
  • 设备测试:开发者可以使用/dev/mtd*设备文件来测试闪存设备的读写性能和可靠性。

注意事项

  • 访问/dev/mtd*设备文件通常需要root权限。
  • 由于MTD设备通常用于存储关键的系统数据,因此在对其进行操作时需要格外小心,以避免数据丢失或系统损坏。

三、/dev/mtdblock* 设备文件

/dev/mtd*不同,/dev/mtdblock*设备文件提供了对MTD设备的块设备接口。这意味着它们可以被挂载为文件系统,并以块为单位进行读写操作。/dev/mtdblock*设备文件的命名方式与/dev/mtd*类似,也使用数字后缀来区分不同的设备。

用途

  • 文件系统挂载/dev/mtdblock*设备文件可以挂载为文件系统,用于存储操作系统、应用程序或用户数据。这对于没有传统硬盘驱动器的嵌入式设备尤其有用。
  • 数据分区:通过使用/dev/mtdblock*设备,开发者可以在闪存设备上创建多个分区,每个分区可以独立地挂载和管理。
  • 系统恢复:在系统崩溃或损坏的情况下,/dev/mtdblock*设备可以用于恢复系统镜像或重要的配置文件。

注意事项

  • 在对/dev/mtdblock*设备进行写操作之前,应确保没有任何文件系统挂载在其上。
  • /dev/mtd*一样,访问/dev/mtdblock*设备文件也需要root权限。

三、这两种设备文件的关系

  这些不同的设备文件通常对应同一块存储区域。比如在使用 NOR Flash 存储器时,不同的设备文件只是提供了不同的访问方式和操作权限,但是它们对应的确实是同一个物理空间或逻辑分区。

  举例来说,当你在一个嵌入式系统中使用 NOR Flash 存储器时,可能会看到 /dev/mtd0/dev/mtdblock0/dev/mtd0ro 这几个设备文件,它们对应的都是 NOR Flash 存储器中的同一个物理空间或逻辑分区。其中 /dev/mtd0/dev/mtdblock0 提供了不同的读写方式,/dev/mtd0ro 则是只读的。这些设备文件允许用户以不同的方式与 NOR Flash 存储器进行交互和访问,例如可以进行烧写、读取和执行代码等操作。

  因此,无论是块设备文件还是字符设备文件,以及只读设备文件,它们都可以对应同一块存储区域或相同的物理空间。区别在于它们提供了不同的权限和访问方式,以满足不同的使用需求。

四、关norflash的一些小知识

  在 NOR Flash 存储器中,一个 block 的大小通常是 64KB(64 * 1024 字节)。这个 block 大小是 NOR Flash 存储器常见的值,但实际上在不同型号的 NOR Flash 存储器中,block 的大小可能会有所不同。因此,在具体使用时,需要查阅相关的数据手册或规格说明来确认所使用的 NOR Flash 存储器 block 的确切大小。

  对于 NOR Flash 存储器来说,通常是在对其进行写操作之前需要先擦除。这是因为 NOR Flash 存储器中的存储单元(如字节或扇区)在执行写操作时,是将数据从逻辑值 1 写入逻辑值 0,因此需要先将要写入的存储单元擦除为逻辑值 1,然后再写入新的数据。

  具体来说,擦除操作会将存储单元的数据全部置为逻辑值 1,而写操作则是将特定的存储单元从逻辑值 1 写入为逻辑值 0。如果在进行写操作时不进行擦除,那么由于 NOR Flash 存储器一般不支持原地写入(in-place write),可能会导致写入的数据无法正确覆盖之前的数据,从而产生错误数据或不稳定的状态。

  因此,为了确保 NOR Flash 存储器中数据的正确性和稳定性,通常在进行写操作之前需要先执行擦除操作。在擦除操作和写操作过程中,还需要注意避免中断电等意外情况,以确保数据的完整性和一致性。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/mtd/mtd.h>
#include <linux/mtddll.h>

#define MTD_DEVICE_0 "/dev/mtd0"
#define MTD_DEVICE_1 "/dev/mtd1"

int main() {
    int mtd0_fd, mtd1_fd;
    struct mtd_info_user mtd_info;
    struct erase_info_user erase_info;
    ssize_t bytes_read, bytes_written;
    char buffer[4096]; // 读取/写入的缓冲区大小

    // 打开第一个MTD设备进行读取
    if ((mtd0_fd = open(MTD_DEVICE_0, O_RDONLY)) < 0) {
        perror("Error opening MTD device 0");
        return EXIT_FAILURE;
    }

    // 获取MTD设备信息
    if (ioctl(mtd0_fd, MEMGETINFO, &mtd_info) < 0) {
        perror("Error getting MTD device information");
        close(mtd0_fd);
        return EXIT_FAILURE;
    }

    // 打开第二个MTD设备进行写入
    if ((mtd1_fd = open(MTD_DEVICE_1, O_WRONLY)) < 0) {
        perror("Error opening MTD device 1");
        close(mtd0_fd);
        return EXIT_FAILURE;
    }

    // 擦除第二个MTD设备的相关块
    for (int i = 0; i < mtd_info.eraseblocks; i++) {
        erase_info.start = i * mtd_info.erasesize;
        erase_info.length = mtd_info.erasesize;

        if (ioctl(mtd1_fd, MEMERASE, &erase_info) < 0) {
            perror("Error erasing MTD device 1");
            close(mtd0_fd);
            close(mtd1_fd);
            return EXIT_FAILURE;
        }
    }

    // 从第一个MTD设备读取数据
    while ((bytes_read = read(mtd0_fd, buffer, sizeof(buffer))) > 0) {
        // 将数据写入第二个MTD设备
        bytes_written = write(mtd1_fd, buffer, bytes_read);
        if (bytes_written != bytes_read) {
            perror("Error writing to MTD device 1");
            close(mtd0_fd);
            close(mtd1_fd);
            return EXIT_FAILURE;
        }
    }

    // 关闭MTD设备文件描述符
    close(mtd0_fd);
    close(mtd1_fd);

    printf("Data transfer from MTD device 0 to MTD device 1 completed successfully.\n");
    return EXIT_SUCCESS;
}

  在这个示例中,我们使用MEMERASEioctl调用来擦除/dev/mtd1设备上的每个擦除块。erase_info_user结构体被用来指定要擦除的块的起始地址和长度。这个操作通常比读取和写入操作慢,因此在实际应用中,您可能希望优化这个过程,例如通过并行擦除或使用更高效的写入策略。

  请记住,擦除操作是破坏性的,因此在执行之前应该确保数据已经备份。此外,确保您有足够的权限来执行这些操作,通常需要root权限。在实际部署之前,应该在受控环境中彻底测试代码,以避免数据丢失。

/*
BLKGETSIZE64 用于获取块设备的大小(以字节为单位),返回的值是一个 unsigned long long 类型
的整数,表示设备的总容量。这个命令通常用于获取大于 2 TB 的设备大小,因为 BLKGETSIZE 在 32 
位系统上可能会出现溢出问题。

BLKGETSIZE 用于获取块设备的大小(以扇区为单位),返回的值是一个 long 类型的整数,表示设备
的总扇区数。通常情况下,这个命令可以满足大多数小于 2 TB 的设备大小获取需求。
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define MTD_BLOCK_DEVICE "/dev/mtdblock0"

int main() {
    int fd;
    unsigned long long erase_size, total_size;
    unsigned int erase_flags = 0; // 通常为0,表示默认擦除选项

    // 打开MTD块设备进行读写
    if ((fd = open(MTD_BLOCK_DEVICE, O_RDWR)) < 0) {
        perror("Error opening MTD block device");
        return EXIT_FAILURE;
    }

    // 获取MTD块设备的擦除大小和总大小
    if (ioctl(fd, BLKGETSIZE64, &total_size) != 0) {
        perror("Error getting device size");
        close(fd);
        return EXIT_FAILURE;
    }
    erase_size = total_size; // 假设整个设备需要擦除

    // 擦除MTD块设备
    if (ioctl(fd, BLKERASE, &erase_size) != 0) {
        perror("Error erasing MTD block device");
        close(fd);
        return EXIT_FAILURE;
    }

    // 准备要写入的数据
    const char *data_to_write = "This is a test.\n";
    size_t data_len = strlen(data_to_write) + 1; // 加1是为了包括字符串的终止符'\0'

    // 从设备开头开始写入数据
    if (write(fd, data_to_write, data_len) != data_len) {
        perror("Error writing to MTD block device");
        close(fd);
        return EXIT_FAILURE;
    }

    // 关闭MTD块设备
    close(fd);

    printf("Data successfully written to MTD block device.\n");
    return EXIT_SUCCESS;
}

  在这个示例中,我们首先打开/dev/mtdblock0设备进行读写操作。然后,我们使用BLKGETSIZE64ioctl调用来获取设备的总大小。接着,我们使用BLKERASEioctl调用来擦除整个设备。最后,我们使用write函数将一个简单的字符串写入设备。

  请注意,这个示例代码假设整个设备都需要擦除,这可能不是所有情况的最佳实践。在实际应用中,您可能需要根据设备的擦除块大小和您的写入需求来计算需要擦除的确切区域。

  在编译和运行此代码之前,请确保您有足够的权限(通常需要root权限),并且/dev/mtdblock*设备文件确实存在于您的系统中。您可以使用以下命令来编译代码:

gcc -o mtdblock_write mtdblock_write.c

  然后,使用以下命令以root用户身份运行编译后的程序:

sudo ./mtdblock_write

  请记住,在对MTD设备进行操作时,您应该非常小心,因为不正确的操作可能会导致数据丢失。在实际部署之前,务必在受控环境中进行彻底的测试。

  欢迎大家指导和交流!如果我有任何错误或遗漏,请立即指正,我愿意学习改进。期待与大家一起进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/488145.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

面试知识汇总——垃圾回收器(分代收集算法)

分代收集算法 根据对象的存活周期&#xff0c;把内存分成多个区域&#xff0c;不同区域使用不同的回收算法回收对象。 对象在创建的时候&#xff0c;会先存放到伊甸园。当伊甸园满了之后&#xff0c;就会触发垃圾回收。 这个回收的过程是&#xff1a;把伊甸园中的对象拷贝到F…

初识redis(一)

前言 引用的是这本书的原话 Redis[1]是一种基于键值对&#xff08;key-value&#xff09;的NoSQL数据库&#xff0c;与很多键值对数据库不同的是&#xff0c;Redis中的值可以是由string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、list&#xff08;列…

Android15功能和 API 概览

Android 15 面向开发者引入了一些出色的新功能和 API。以下部分总结了这些功能&#xff0c;以帮助您开始使用相关 API。 如需查看新增、修改和移除的 API 的详细列表&#xff0c;请参阅 API 差异报告。如需详细了解新的 API&#xff0c;请访问 Android API 参考文档&#xff0…

Selenium 自动化 —— 定位页面元素

更多内容请关注我的 Selenium 自动化 专栏&#xff1a; 入门和 Hello World 实例使用WebDriverManager自动下载驱动Selenium IDE录制、回放、导出Java源码浏览器窗口操作切换浏览器窗口 使用 Selenium 做自动化&#xff0c;我们不仅仅是打开一个网页&#xff0c;这只是万里长…

Stable Diffusion 进阶教程 - 二次开发(制作您的文生图应用)

目录 1. 引言 2. 基于Rest API 开发 2.1 前置条件 2.2 代码实现 2.3 效果演示 2.4 常见错误 3. 总结 1. 引言 Stable Diffusion作为一种强大的文本到图像生成模型&#xff0c;已经在艺术、设计和创意领域引起了广泛的关注和应用。然而&#xff0c;对于许多开发者来说&#xff…

时序预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络时间序列预测

时序预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络时间序列预测 目录 时序预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现SSA-BP麻雀算法优化BP神经网络时间序列预测&#xff08;完整源码和数据…

DRC检查及丝印的调整

DRC检查及丝印的调整 综述&#xff1a;本文主要讲述AD软件中DRC检查、丝印的调整以及logo的添加的相关步骤&#xff0c;附加logo添加的脚本链接和大量操作图片&#xff0c;使步骤详细直观。 1. 点击“工具”→“设计规则检查”→“运行DRC”。&#xff08;一开始可以只开启电…

利用云手机技术,开拓海外社交市场

近年来&#xff0c;随着科技的不断进步&#xff0c;云手机技术逐渐在海外社交营销领域崭露头角。其灵活性、成本效益和全球性特征使其成为海外社交营销的利器。那么&#xff0c;究竟云手机在海外社交营销中扮演了怎样的角色呢&#xff1f; 首先&#xff0c;云手机技术能够消除地…

LLM - 大语言模型的指令微调(Instruction Tuning) 概述

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/137009993 大语言模型的指令微调(Instruction Tuning)是一种优化技术&#xff0c;通过在特定的数据集上进一步训练大型语言模型(LLMs)&a…

javaWeb个人日记(博客)管理系统

一、简介 在快节奏的生活中&#xff0c;记录生活点滴、感悟和思考是一种重要的方式。基于此&#xff0c;我设计了一个基于JavaWeb的个人日记本系统&#xff0c;旨在帮助用户轻松记录并管理自己的日记。该系统包括登录、首页、日记列表、写日记、日记分类管理和个人中心等功能&…

mysql - 缓存

缓存 InnoDB存储引擎在处理客户端的请求时&#xff0c;当需要访问某个页的数据时&#xff0c;就会把完整的页的数据全部加载到内存中&#xff0c;也就是说即使我们只需要访问一个页的一条记录&#xff0c;那也需要先把整个页的数据加载到内存中。将整个页加载到内存中后就可以…

命令模式(请求与具体实现解耦)

目录 前言 UML plantuml 类图 实战代码 模板 Command Invoker Receiver Client 前言 命令模式解耦了命令请求者&#xff08;Invoker&#xff09;和命令执行者&#xff08;receiver&#xff09;&#xff0c;使得 Invoker 不再直接引用 receiver&#xff0c;而是依赖于…

Java基础--128陷阱

问题引入 Integer a 123; Integer b 123; System.out.println(ab); 结果为true。 但是如果代码如下 Integer a 1230;Integer b 1230;System.out.println(ab); 这个的结果就是false。 问题解决 当Integer a 123时&#xff0c;其实他底层自动转换成了Integer a Inte…

Learn OpenGL 29 延迟着色法

延迟着色法 我们现在一直使用的光照方式叫做正向渲染(Forward Rendering)或者正向着色法(Forward Shading)&#xff0c;它是我们渲染物体的一种非常直接的方式&#xff0c;在场景中我们根据所有光源照亮一个物体&#xff0c;之后再渲染下一个物体&#xff0c;以此类推。它非常…

网络安全-文件包含

一、php://input 我们先来看一个简单的代码 <meta charset"utf8"> <?php error_reporting(0); $file $_GET["file"]; if(stristr($file,"php://filter") || stristr($file,"zip://") || stristr($file,"phar://&quo…

Windows如何搭建 ElasticSearch 集群

单机 & 集群 单台 Elasticsearch 服务器提供服务&#xff0c;往往都有最大的负载能力&#xff0c;超过这个阈值&#xff0c;服务器 性能就会大大降低甚至不可用&#xff0c;所以生产环境中&#xff0c;一般都是运行在指定服务器集群中。 除了负载能力&#xff0c;单点服务器…

Redis到底是多线程还是单线程?

Redis6.0之前&#xff1a;是单线程模式。 Redis6.0之后&#xff1a;Redis的IO线程是多线程&#xff0c;worker线程是单线程。 Redis6.0之前&#xff1a;单线程 Redis6.0之后&#xff1a;Redis的IO线程是多线程&#xff0c;worker线程是单线程。

iOS开发进阶(九):OC混合开发嵌套H5应用并互相通信

文章目录 一、前言二、嵌套H5应用并实现双方通信2.1 WKWebView 与JS 原生交互2.1.1 H5页面嵌套2.1.2 常用代理方法2.1.3 OC调用JS方法2.1.4 JS调用OC方法 2.2 JSCore 实现原生与H5交互2.2.1 OC调用H5方法并传参2.2.2 H5给OC传参 2.3 UIWebView的基本用法2.3.1 H5页面嵌套2.3.2 …

面试算法-101-重排链表

题目 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交…

使用mid360从0开始搭建实物机器人入门级导航系统,基于Fast_Lio,Move_Base

Introduction 本文原本只是自己在拿到mid360后&#xff0c;开始进行开发过程的一些问题和学习的记录。毕竟实物和仿真还是有很多不同&#xff0c;且由于碰到的问题也比较多&#xff0c;READEME也越来越详细&#xff0c;所以就干脆整合起来&#xff0c;做成了一篇使用mid360的搭…