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

文件系统I/O FATFS RW 源码分析

文件系统I/O FATFS RW 源码分析

0 参考

FatFs 是用于小型嵌入式系统的通用 FAT/exFAT 文件系统模块。FatFs 整个项目都按照 ANSI C (C89) 编写。与存储器 I/O 解耦良好,便于移植到 8051、PIC、AVR、ARM、Z80、RX 等小型微控制器中。

下面是关于 FAT 文件系统格式和 FATFS 项目的文档链接。

  1. FatFs - Generic FAT Filesystem Module
  2. The basics of FAT filesystem
  3. Microsoft Extensible Firmware Initiative FAT32 File System Specification

1 如何写入

FATFS 提供了一系列使用例程,通过 FATFS 接口把数据写入文件系统的流程如下:

  1. 挂载文件系统
f_mount(&FatFs, "", 0);		/* Give a work area to the default drive */
  1. 根据文件路径打开文件
fr = f_open(&Fil, "newfile.txt", FA_WRITE | FA_CREATE_ALWAYS);	/* Create a file */
  1. 把数据写入文件
f_write(&Fil, "It works!\r\n", 11, &bw);	/* Write data to the file */
  1. 关闭文件
fr = f_close(&Fil);							/* Close the file */

如上,FATFS 的使用和我们在桌面操作系统上读写文件大差不差,使用这个包可以让我们在在 MCU 上存取数据时获得操作系统级的体验。下面是一个完整的 DEMO.

example

/*----------------------------------------------------------------------*/
/* Foolproof FatFs sample project for AVR              (C)ChaN, 2014    */
/*----------------------------------------------------------------------*/

#include <avr/io.h>	/* Device specific declarations */
#include "ff.h"		/* Declarations of FatFs API */

FATFS FatFs;		/* FatFs work area needed for each volume */
FIL Fil;			/* File object needed for each open file */

int main (void)
{
	UINT bw;
	FRESULT fr;


	f_mount(&FatFs, "", 0);		/* Give a work area to the default drive */

	fr = f_open(&Fil, "newfile.txt", FA_WRITE | FA_CREATE_ALWAYS);	/* Create a file */
	if (fr == FR_OK) {
		f_write(&Fil, "It works!\r\n", 11, &bw);	/* Write data to the file */
		fr = f_close(&Fil);							/* Close the file */
		if (fr == FR_OK && bw == 11) {		/* Lights green LED if data written well */
			DDRB |= 0x10; PORTB |= 0x10;	/* Set PB4 high */
		}
	}

	for (;;) ;
}

2 f_write()里做了什么

2.1 逐行分析源码

为了搞清楚 FATFS 的写操作逻辑,我们需要逐行分析 f_write() 的实现。

查看源码,立即就能发现 f_write() 调用了一个名为 disk_write 的函数,这很可能就包含着操作 磁盘/SD 卡等存储介质的底层实现。

if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);

disk_write() 的原型如下:

DRESULT disk_write (
	BYTE drv,			/* Physical drive nmuber (0) */
	const BYTE *buff,	/* Pointer to the data to be written */
	LBA_t sector,		/* Start sector number (LBA) */
	UINT count			/* Sector count (1..128) */
)

参数 buffcount 很好理解,就是数据缓存和数据大小,无需多言。

参数 sector 则由“簇数” fp->clust计算得到:

sect = clst2sect(fs, fp->clust);	/* Get current sector */

fp->clustfp->obj 中读出, 这个变量在 f_open() 中被初始化。

clst = fp->obj.sclust;	/* Follow from the origin */
...
fp->clust = clst;			/* Update current cluster */

drv 的值即 disk_write() 的第一个参数 fs->pdrv 的值, fs 在 f_write() 开头的有效性检查 validate() 那里被初始化,初始化时刻如下所示:

static FRESULT validate (	/* Returns FR_OK or FR_INVALID_OBJECT */
	FFOBJID* obj,			/* Pointer to the FFOBJID, the 1st member in the FIL/DIR structure, to check validity */
	FATFS** rfs				/* Pointer to pointer to the owner filesystem object to return */
){
...
    *rfs = (res == FR_OK) ? obj->fs : 0;	/* Return corresponding filesystem object if it is valid */
	return res;
}

2.2 总结一下 f_write()

做了什么:

  • 解析在 f_open() 中初始化的 fp
  • 获取给定文件路径对应的簇号(cluster number)并转换成扇区号(sector number)
  • 获取文件系统的物理驱动号(即fp 对应的区域在哪个物理设备/磁盘上)
  • 以驱动号、扇区号、数据和数据大小为参数调用 disk_write()

3 我们实际上关心的是什么

依据上面的分析,我们已经知道我们可以通过修改 disk_write() 的实现来适配不同的存储设备。

但我们真正关心的是,如何在磁盘上找到某个文件,所以我们还需要分析 f_open() 的实现。

3.2 f_open() 里做了什么

下面是 f_open 运行时的调用树,看起来整个 open 的过程像是在树状结构中做检索。我想那么,也许有必要看看 FAT 文件系统的原始定义,也即文件系统的镜像格式。

f_open()

follow_path()

create_name() // iterator of dir name in file path
dir_find()

dir_sdi()

get_fat() // get fat32 entry, ret cluster number
clst2sect() // transfrom cluster number to section number

回到文件系统的镜像格式

FAT(File Allocation Table),最早在DOS v1.0 中被引入,是一种极简的文件系统,占用空间,是目前最常见的文件系统之一。FAT 文件系统有多种历史版本,比如 FAT12/FAT16/FAT32/exFAT/VFAT,这里只介绍 FAT32,

使用 FAT 管理的存储介质分为三个基本区域:

  • 启动记录 (The boot record)
  • 文件分配表 (FAT,The File Allocation Table)
  • 目录和数据区(The directory and data area)

“The boot record”(引导记录)是指存储在磁盘的第一个扇区的特殊区域。它也被称为引导扇区(boot sector)或主引导记录(master boot record,MBR)。

FAT32 的主引导扇区可分为两部分,前 36 字节与其他版本的 FAT 一致,36 字节以后的区域划分如下图所示。

在这里插入图片描述

origin_url=.%2Fbehind36.png&pos_id=img-nzYYNo5c-1710653498658)

偏移 44 字节处为根目录所在的扇区(通常为2),打开文件系统镜像,转到对应扇区,可见目录中的文件列表如下。

在这里插入图片描述

额外提一句,FAT 文件系统中的时间记录以 1980 年 1 月 1 日为基准,如上图偏移0x10位置的两个字节为0X3C21,高 7 位表示年,数值为30,对应的年份为 1980+30=2010 年。

在这里插入图片描述


http://www.kler.cn/a/273203.html

相关文章:

  • CesiumJS 案例 P19:添加矩形、监听鼠标左击、监听鼠标右击、监听鼠标移动
  • docker安装低版本的jenkins-2.346.3,在线安装对应版本插件失败的解决方法
  • 【算法】【优选算法】双指针(下)
  • Linux操作系统 ------(3.文本编译器Vim)
  • 【论文阅读笔记】Wavelet Convolutions for Large Receptive Fields
  • Elasticsearch 实战应用详解!
  • win修改图标自定义QQ桌面图标
  • 粤嵌6818开发板通过MobaXterm使用SSH连接开发板
  • 前端入职配置新电脑!!!
  • 力扣思路题:最长特殊序列1
  • kingbase 服务器配置(参数修改)
  • 关于学习的一点粗浅见解
  • Linux TCP参数——tcp_adv_win_scale
  • luceda ipkiss教程 63:器件端口延伸ExtendPorts
  • C++——字符串、读写文件、结构体、枚举
  • 【人工智能】英文学习材料03(每日一句)
  • 【字符串算法题】541. 反转字符串 II
  • es 聚合操作(二)
  • openstack迁移虚拟机--来自gpt
  • kerberos验证协议安装配置使用
  • 6语言交易所/多语言交易所php源码/微盘PHP源码
  • 数据结构 二叉树 力扣例题AC——代码以及思路记录
  • 由浅到深认识C语言(13):共用体
  • 分享一个不错的three.js开源项目
  • 鸿蒙 Harmony 初体验
  • Linux——动静态库的制作及使用与动态库原理