全网独一份!MicroPython 存储 + 文件系统底层原理,新手也能看懂的完整指南

1.MicroPython 的存储区域

在学习 MicroPython 的过程中,了解它的 “存储区域” 就像搞清楚我们的程序 “住” 在哪里、不同数据 “放” 在哪个柜子里,只有知道数据的存放规则,才能避免写出内存溢出、数据莫名出错的代码,也能理解程序运行的底层逻辑。

先补充两个新手必知的基础概念:

  • Flash(闪存​​​:可以理解成 “电脑的硬盘”,断电后数据不会丢,读写速度相对慢一点,适合存长期不变的内容。MicroPython 的固件、我们写的程序代码都会存在这里。
  • ​SRAM​(​​静态随机存取存储器)​:可以理解成 “电脑的内存条”,断电后数据就没了,但读写速度极快,适合存程序运行时临时要用、会频繁变化的数据。

MicroPython 的存储区域分布类似于传统编程语言(C/C++)的内存模型,核心分为 Flash(闪存)和 SRAM(静态随机存取存储器)两大块,每块包含不同功能的子区域,各司其职:

  • Flash 里的内容像刻在光盘上的电影,一旦确定就不会随便修改,断电后数据依然保留,主要存储 “不会变的内容”:
    • 代码区(Code Section)​:这里存储了程序代码,即字节码或编译后的机器码以及底层的解释器代码,它在运行时不变,不会被修改,属性为只读;
    • ​只读​数据区(​​Read-Only​ Data Section)​:这里存放常量数据,比如字符串字面量(如 "hello MicroPython")、模块级别的常量变量(如模块开头定义的 MAX_NUM = 100),数据在程序运行过程中不会被修改,属于静态内容,可以在整个程序运行期间重复使用( 比如你在程序里多次用到 "hello" 这个字符串,它只会在这个区域存一份,避免重复占用空间)。
  • SRAM 像办公桌上的临时草稿纸,可随时修改,断电(如拔电源)后内容消失,主要存储程序运行时 “临时要用、会变化的内容”:
    • 数据区(Data Section)​:存放 MicroPython 解释器的全局变量(整个程序中都能访问的变量),这些变量从程序启动时加载,直到程序结束(或设备断电)才消失。
      • 你在程序最外层定义的 total = 0,这个 total 就存在数据区,无论在哪个函数里修改它,都是操作这个区域里的值。
    • 堆区(Heap​​​ Section)​:SRAM 中最灵活的区域,专门存储 MicroPython 的 “动态对象” 和动态分配的数据:比如列表([1,2,3])、字典({"name":"小明"})、自定义类的实例(如 class Car: ...; my_car = Car()),以及函数内部的局部变量。
      • 解释器通过 “内存分配器” 从堆区按需分配空间(比如新建列表时,就从堆区划一块空间给这个列表);
      • 自带 “垃圾回收器(GC)”,如果某个对象(如不用的列表)没有被任何代码引用,GC 会自动回收它占用的堆区空间,避免内存被占满。
    • 栈区(Stack Section)​:专门处理函数调用相关的内容,比如递归函数调用、函数内的局部变量(如 def calc(): num=10; print(num) 里的 num)。
      • 栈的总大小有限(不同硬件的 SRAM 容量不同,栈区配额也不同);
      • 栈大小随函数调用 / 返回动态调整:调用函数时,栈区为该函数的局部变量分配空间;函数执行完返回后,空间会被释放;
      • 风险提示:若递归调用层数过多(如无限递归函数),栈区空间会被占满,触发 “栈溢出错误”,导致程序崩溃,就像草稿纸写满后,再写会溢出到桌子外一样。

2.MicroPython 中的文件系统

2.1 文件系统的概念和组成结构

文件系统是操作系统用来管理存储介质(如硬盘、固态硬盘、闪存等)上文件的结构和方法,它负责文件的创建、读取、写入、删除以及目录的组织和管理,使得用户和应用程序能够以结构化的方式存取和管理数据。

你可以把 “文件系统” 理解成设备(比如 U 盘、嵌入式开发板的存储)用来存文件的 “整理规则”—— 就像你给抽屉定好 “哪层放文档、哪层放照片” 的规则,设备靠文件系统来管理 “文件存在哪、怎么找到它、怎么存更高效”。

1.PNG

通常情况下,文件系统具有以下功能:

  • 文件管理​:包括创建文件、读取文件、写入文件和删除文件等操作;
  • 目录管理​:包括创建目录、读取目录和删除目录等操作;
  • 存储管理​:包括为文件分配存储空间和跟踪存储使用等操作。

FAT 是其中一种简单易懂的文件系统(常用在嵌入式设备里),先补充一个新手必知的基础概念:

  • ​:文件系统存储数据的 “最小单位”:
    • 把存储设备分成很多大小一致的 “小格子”,每个格子就是一个簇;
    • 一个文件会占用 1 个或多个 “小格子”(簇),哪怕文件很小,也会占至少 1 个簇。

以 FAT 文件系统为例,一个完整的文件系统通常由以下部分组成:

2.PNG
文件系统将磁盘空间划分为若干个簇,以便于管理和组织存储的数据,簇(Cluster)是一个基本的存储单位,每个簇包含一个或多个扇区(Sector),而每个扇区是存储设备中最小的存储单元。簇的大小在不同的文件系统和存储设备上可能不同,对于 FAT12/FAT16 来说,簇的大小通常为 512 字节、1KB、2KB 或 4KB

  • ​引导扇区(Boot Sector):​文件系统的 “说明书”,对应图里的 “引导扇区” 区域,它是存储设备的 “第一份信息”,像抽屉的 “使用说明书”,里面记录了:
    • 文件系统类型(是 FAT12/FAT16 还是 FAT32);
    • 每个簇占多少空间、FAT 表的位置、根目录的大小(对应图里 Boot Sector 里的 “FAT type、FAT size、Root Directory size”)。
    • 设备启动时,先读这个 “说明书”,才知道怎么识别后面的文件。
  • ​目录表(Directory Table):​文件的 “清单列表”,对应图里的 “Root Directory(根目录表)”,它是存储设备的 “文件清单”,记录了每个文件的关键信息:
    • 文件名(比如图里的 tyui.jpgmes.doc);
    • 文件大小(比如 tyui.jpg 是 1400B);
    • 文件存在哪个簇里(“起始簇号”,比如 tyui.jpg 从簇 2 开始存);
    • 你要找文件时,先查这份 “清单”,就能知道 “文件叫啥、多大、从哪个簇开始找”。
  • ​文件分配表(FAT):​簇的 “去向地图”,对应图里的 “FATs” 区域,它是一张 “簇的关系表”(可以理解为数组),因为一个文件可能占多个簇(比如大文件),FAT 会记录 “每个簇的下一个簇是谁”:
    • 表中的每个条目对应一个簇,值的含义是:
      • 0x0000:这个簇是空的(可用);
      • 0xFFFF:这个簇是文件的最后一个簇(结束标记);
      • 0xFF7F:这个簇是坏的(不能存数据)。
    • tyui.jpg 的起始簇是 2:
      • 看 FAT 里 “2 对应的是 3”;
      • 簇 2 的下一个簇是 3;
      • “3 对应的是 4”;
      • 簇 3 的下一个簇是 4;
      • “4 对应的是 0xFFFF”;
      • 簇 4 是最后一个簇。所以 tyui.jpg 存在簇 2、3、4 里。
    • mes.doc 的起始簇是 5:
      • FAT 里 “5 对应 6”;
      • 簇 5 的下一个是 6;
      • “6 对应 0xFFFF”
      • 结束。所以 mes.doc 存在簇 5、6 里。
  • 数据区(Data Area)​:文件的 “实际存放区”,对应图里的 “Data Area”,是存储设备里真正放文件内容的地方,所有文件的实际数据(比如 tyui.jpg 的图片内容、mes.doc 的文档内容)都存在这里的簇里。
    • 前面的 “清单” 和 “地图” 都是辅助,真正的文件内容就存在这些簇里。

常见的文件系统类型包括 FAT(FAT12、FAT16 和 FAT32)、NTFS、ext 和 LittleFS 等,其中 FAT 文件系统是嵌入式设备常用的文件系统,具有设计简单,易于实现和理解和开销小等特点,但是:

  • FAT 系统支持的最大文件大小较小(比如 FAT16 最大只支持 2GB 文件);;
  • 同时在使用过程中可能会产生碎片化(文件存的时候东一个簇、西一个簇,找起来变慢),影响性能;
  • 并且没有内置的权限(谁都能改文件)和安全机制,对数据的保护较弱(文件坏了难恢复)。

在 MicroPython 中,最常用的文件系统是 FAT、LittleFS v1 和 LittleFS v2 这三种文件系统,对比如下:

3.png

2.2 VFS 虚拟文件系统

先补 2 个新手必懂的基础概念,帮你快速理解 VFS:

  • 抽象层​:可以理解成 “统一翻译官”,不管底层是啥 “方言”(不同文件系统),它都转换成一套 “通用语言”(统一的操作方法)给你用。
  • 块设备​:存储设备的 “通用称呼”(比如 SD 卡、开发板自带的闪存),这类设备是按固定大小的 “块” 来读写数据的,是文件系统的 “载体”。

在 MicroPython 中,VFS(Virtual File System,虚拟文件系统)是一个用于在嵌入式设备上提供文件系统支持的模块,它是一个抽象层,提供统一的文件系统接口:

  • 使得应用程序可以通过相同的 API 访问不同类型的底层文件系统(如 FAT、SPIFFS、LittleFS 等),而应用程序无需关心底层文件系统的具体实现;
  • 简单来说就是,不管你用的是 SD 卡的 FAT 系统、开发板自带的 LittleFS 系统,VFS 都给你一套一模一样的操作方法(比如存文件、读文件),不用因为换了存储设备就重新学新命令。

并且,用户可以将不同类型的文件系统挂载到特定的路径,并在需要时卸载它们:你可以把 “挂载” 理解成给存储设备 “安个入口”—— 比如把 SD 卡 “接” 到系统里的 /sd 文件夹下,之后只要访问 /sd 就能操作 SD 卡;“卸载” 则是安全地 “拔掉” 这个入口,避免数据损坏。

例如,我们可以将一个 SD 卡的 FAT 文件系统挂载到 /sd,同时将一个 SPIFFS 文件系统挂载到 /spiffs,并通过统一的文件操作接口进行文件管理;通过 VFS 模块,我们也可以在软件层面创建一个块设备(按块操作读写的设备),用于模拟存储设备的行为,例如可以在嵌入式系统或操作系统中创建一个虚拟的 RAM 块设备。

VFS 提供了如下文件系统挂载与卸载方法:

  • vfs.mount(fsobj, mount_point, *, readonly):给存储设备 “安入口”
    • 描述​:把一个文件系统对象(比如 FAT/LittleFS),“挂” 到系统里的某个路径下(比如 /sd),之后就能通过这个路径操作存储设备。
    • 参数大白话​:
      • fsobj:要挂载的文件系统对象(比如后面会讲的 FAT/LittleFS 对象),或者直接填块设备(比如 SD 卡的对象)。
      • mount_point:“入口路径”—— 比如用 '/' 代表系统根目录,用 '/sd' 代表 SD 卡的专属入口。
      • readonly(可选):选 True 的话,这个存储设备只能读不能改;选 False(默认)可以读写。
    • 异常提示​:如果这个 “入口路径” 已经被别的设备占了(比如 /sd 已经挂了另一个 SD 卡),会报错 OSError(EPERM)
  • vfs.umount(mount_point):安全 “拔入口”
    • 描述​:把之前挂载的文件系统 “卸下来”,比如用完 SD 卡后卸载,避免直接拔卡丢数据。
    • 参数​:填要卸载的 “入口路径”(比如 '/sd'),或者直接填之前挂载的文件系统对象。
    • 异常提示​:如果填的 “入口路径” 根本没挂载过设备,会报错 OSError(EINVAL)

VFS 只是 “总管”,得先创建对应类型的文件系统对象(比如 FAT/LittleFS),才能挂载使用。下面是常用的文件系统类(对应你提供的类说明)。

以下是文件系统实现类,可以实现构建 FAT、LittleFS v1、LittleFS v2 等文件系统:

4.png

5.png

如下所示:

  • vfs.VfsFat(block_dev):创建 FAT 文件系统对象
    • 作用​:生成一个用 FAT 格式管理存储的对象(比如 SD 卡常用 FAT 格式),block_dev 是要管理的块设备(比如 SD 卡对象)。
    • mkfs 方法​:这是个 “格式化” 功能 —— 比如 vfs.VfsFat.mkfs(sd_dev),就是把 SD 卡(sd_dev)格式化成 FAT 文件系统,之后才能存文件。
  • vfs.VfsLfs1(block_dev, ...) / vfs.VfsLfs2(...):创建 LittleFS 文件系统对象
  • LittleFS 是嵌入式设备常用的 “轻量文件系统”(比 FAT 更适合小闪存),分 v1 和 v2 两个版本:
    • 作用​:生成用 LittleFS 格式管理存储的对象,block_dev 是对应的块设备(比如开发板自带的闪存)。
    • 参数(可选)​:readsize/progsize 等是读写 / 写入的块大小(新手用默认值 32 就行);v2 版本的 mtime 参数是用来记录文件修改时间的。
    • mkfs 方法​:比如 vfs.VfsLfs2.mkfs(flash_dev),就是把开发板闪存(flash_dev)格式化成 LittleFS v2 系统。
  • vfs.VfsPosix(root=None):访问电脑的文件系统(仅调试用)
    • 作用​:在电脑上运行 MicroPython 时,用来访问电脑本身的文件系统(比如电脑的 C 盘、D 盘),root 填电脑里的路径(比如 'C:/test'),不填的话默认用当前文件夹。

下表显示了默认情况下不同芯片中的 MicroPython 固件中包含的文件系统:

6.png

2.3 块设备

文件系统建立在块设备之上,块设备是一种存储设备,它将存储空间划分为固定大小的块,并允许对这些块进行随机访问,即每个块都有一个唯一的地址,可以独立地进行读写操作;简单来说,块设备通过块来存储数据,文件系统将这些块组织成文件和目录,使用户可以方便地访问和管理数据:

  • 它把存储空间分成很多大小一致的 “块”(比如每个块 1KB),每个块有唯一的 “逻辑块地址”(图里的 0、1、2、3…);
  • 读写数据时,直接指定 “块地址” 就能操作对应块 —— 比如图里写数据到地址 3 的块、读地址 8 的块,不用按顺序从 0 开始翻,想操作哪个块直接找它的编号就行。
  • 文件系统(比如 FAT、LittleFS)就是把这些 “块” 组织成文件 / 目录的 —— 比如一个文件可能占块 3、块 4、块 5,文件系统帮你记录 “这个文件对应哪些块”。

7.png

8.png

MicroPython 没有提供块设备的操作方法,但是提供了一个抽象的块设备接口 vfs.AbstractBlockDev 类,这个模板规定了块设备必须实现的方法(比如 “读某个块的数据”“写数据到某个块”),相当于 “考试答题卡的格式”只有按模板写代码,MicroPython 才能识别这是个块设备,进而在它上面建文件系统。

实际的块设备类必须实现下面描述的方法:

9.png

posted @ 2026-09-03 16:53  FreakStudio  阅读(18)  评论(0)    收藏  举报