GHOST系统之家 - Windows系统光盘下载网站!
当前位置:GHOST系统之家>系统教程 > littlefs原理分析—文件读写(五)

littlefs原理分析—文件读写(五)

来源:Ghost系统之家浏览:时间:2023-08-21 09:54:55

littlefs原理分析—文件读写(五)

作者:深开鸿Kaihong 2022-11-15 09:24:16系统 OpenHarmony 本文介绍了littlefs中的文件读写机制,到这里littlefs大部分的操作就都已经做了分析了。

​​想了解更多关于开源的内容,请访问:​​

​​51CTO开源基础软件社区​​

​​https://ost.51cto.com​​

前言

上一篇文章介绍了littlefs中的目录操作,这一篇文章则将介绍littlefs中的文件读写操作。

本文会根据文件的存储类型进行介绍,即inline文件和outline文件,其读写过程也有差别。另外还会介绍inline文件到outline文件的转换,以及littlefs底层的读写API。

1、inline文件读写

因为inline文件数据存储于其父目录的元数据中,inline文件的读写实际上通过commit机制实现。读是通过遍历tag,写则是通过commit一个INLINESTRUCT类型的tag。

对于inline文件的数据读取,实际上就是从其父目录的元数据中进行读取,其过程已在commit机制中描述。

对于inline文件的写入,即commit一个INLINESTRUCT类型的tag,大致过程如下:

littlefs原理分析#[五]文件读写-开源基础软件社区

2、inline文件转outline文件

当文件大小超过1/8 block_size、或超过文件cache大小时,inline文件会转为outline文件,该转换过程在文件写入过程中触发。inline文件转为outline文件之后就不会再转回inline文件,即使对文件进行truncate操作。

转换过程步骤如下:

为文件重分配块,将inline数据写入块中。commit一个新的CTZSTRUCT类型的tag。

commit过程如下图:

littlefs原理分析#[五]文件读写-开源基础软件社区

其中,CTZSTRUCT类型的tag中包含了新分配的文件跳表头节点的块指针。当读取文件,遍历tag时,检测到CTZSTRUCT,就会从其中文件跳表头节点的块指针读取文件数据。具体跳表中读写文件的过程在下小节中说明。

3、outline文件读写

回顾outline文件的存储结构,其数据是用一个跳表进行存储的:

littlefs原理分析#[五]文件读写-开源基础软件社区

outline文件的读写通过跳表的机制完成,commit时只需要commit带有更新后的跳表头的CTZSTRUCT tag。下面进行具体说明。

(1)outline文件读操作

读取数据的步骤如下:

调用lfs_ctz_find找到目标数据所在的块。调用lfs_bd_read进行读取,该函数在后文进行分析。

其中,lfs_ctz_find函数从头节点开始,通过块头处储存的跳表节点块指针进行遍历、寻找目标块位置。

跳表中块指针按固定规律分布:对block n,如果n可以被2^x整除,那么该block就含有一个指向block n-2^x的块指针。以block 4为例:

4可以被2^0整除,则block 4含有4-2^0即block 3的块指针。4可以被2^1整除,则block 4含有4-2^1即block 2的块指针。4可以被2^2整除,则block 4含有4-2^2即block 0的块指针。

由此规律,又因为块的大小是固定的,那么只要知道文件的偏移位置,就可以获取该偏移位置所在block在跳表中的序号、该块上有几个块指针等信息。lfs_ctz_find函数就是根据此规律进行查找:

获取跳表中块序号:根据文件偏移和块大小计算,相关函数为lfs_ctz_index获取块头部块指针数量:用ctz指令,ctz(块序号)

(2)outline文件写操作

outline文件写入数据时又分为两种情况,其写入步骤也不同:

如果写入数据后不超过当前块,则调用lfs_bd_prog进行写入。该步骤相对简单。如果写入数据后超过当前块:调用lfs_ctz_find找到写入位置所在的块。调用lfs_ctz_extend在写入位置插入新的头节点。最后当调用lfs_file_sync或lfs_file_close时进行commit,实际将更新后的CTZSTRUCT tag写入元数据。

当数据写入后超过当前块时,会涉及到跳表的更新,下面着重对这种情况进行说明。

lfs_ctz_extend

lfs_ctz_extend函数的作用是在文件写入的位置插入新的头节点。其步骤如下:

分配一个新块作为新的头节点,并调用lfs_bd_prog将原头节点块中的数据复制到新块中。下图中,调用lfs_bd_prog传入的pcache参数为file->cache,lfs_bd_prog会先将数据写入到file->cache中,等到需要进行flush操作时才将数据实际写回block。

littlefs原理分析#[五]文件读写-开源基础软件社区

将新的头节点与左边的后继结点链接,右边的旧的前继节点被舍弃(但块中内容不会被立即擦除):

littlefs原理分析#[五]文件读写-开源基础软件社区

注:如果文件写入位置位于文件末尾,则图示中ctz block即为旧头节点。调用lfs_file_seek函数可改变文件写入位置。

commit后会写入新的CTZSTRUCT tag,其过程如下:

littlefs原理分析#[五]文件读写-开源基础软件社区

COW策略

outline文件写入数据时是COW(copy-on-write)策略,lfs_ctz_extend函数插入新的头节点时并不会将旧头节点与后继节点的链接断掉。只有当最后将新的CTZSTRUCT tag写入其父目录的元数据中后,新的CTZSTRUCT tag中所包含的outline文件跳表头节点才更新成功。

因此,如果发生掉电等异常情况导致outline文件的写入操作未能完成时,其原有的数据也不会被丢弃。

如下图,outline文件插入新的节点时不会去破坏原有的块的数据。只有commit完成后,才会将新的头节点写入父目录的元数据中,将原来的头节点覆盖。

littlefs原理分析#[五]文件读写-开源基础软件社区

4、block device读写

littlefs中block device相关的读写操作是其他各种上层读写操作的基础,前文中提到的文件读写等操作均由block device相关的读写操作完成。block device相关读写操作是直接对具体的块进行操作。文件读写、元数据commit过程中都是通过调用了block device相关的读写操作完成的。主要的相关函数为:

lfs_bd_read:从源块或cache中读取数据。lfs_bd_prog:写入数据到目标块或cache。lfs_bd_flush:把cache中数据写入到块中。文件写入后,只有当进行文件flush、sync或关闭操作时,才会调用lfs_bd_flush将数据实际写入块中,并将所有的更改进行commit。

以上函数利用cache或直接从块中进行读写。

当直接从块中进行读写时,是调用了用户配置中提供的相关读写函数:

// Configuration provided during initialization of the littlefsstruct lfs_config {...// Read a region in a block. Negative error codes are propogated// to the user.int (*read)(const struct lfs_config *c, lfs_block_t block,lfs_off_t off, void *buffer, lfs_size_t size);// Program a region in a block. The block must have previously// been erased. Negative error codes are propogated to the user.// May return LFS_ERR_CORRUPT if the block should be considered bad.int (*prog)(const struct lfs_config *c, lfs_block_t block,lfs_off_t off, const void *buffer, lfs_size_t size);// Erase a block. A block must be erased before being programmed.// The state of an erased block is undefined. Negative error codes// are propogated to the user.// May return LFS_ERR_CORRUPT if the block should be considered bad.int (*erase)(const struct lfs_config *c, lfs_block_t block);// Sync the state of the underlying block device. Negative error codes// are propogated to the user.int (*sync)(const struct lfs_config *c);...};

(1)cache

block device读写函数均接受两个cache,即rcache和pcache作为参数,用作读缓存和写缓存。具体作用见后面分析。

littlefs中cache共有以下几种:

全局rcache,lfs->rcache。用作rcache参数。全局pcache,lfs->pcache。读写元数据时用作pcache参数。文件的cache,file->cache。当对文件进行读写操作时用作pcache参数。

(2)block device读操作

lfs_bd_read将源块中数据读到目标buffer中。读取过程中,根据数据是否在缓存中,分为以下几种情况:

在pcache或rcache中:直接从cache中复制。

littlefs原理分析#[五]文件读写-开源基础软件社区

不在pcache和rcache中,且所需读取大小小于一次能加载到cache中数据的大小:将源块中数据加载到rcache,以便后面从rcache中读。

littlefs原理分析#[五]文件读写-开源基础软件社区

不在pcache和rcache中,且所需读取大小不小于一次能加载到cache中数据的大小:直接从源块中读。

littlefs原理分析#[五]文件读写-开源基础软件社区

相关函数:

lfs_bd_read(lfs_t *lfs,| const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint,| lfs_block_t block, lfs_off_t off,| void *buffer, lfs_size_t size) | // 1. 检查是否已读完,未读完则继续步骤,否则结束|-> while (size > 0) ...|| // 2. 如果pcache中有缓存对应数据,则从pcache中读|-> if (pcache && block == pcache->block &&| off < pcache->off + pcache->size) {| if (off >= pcache->off) {| // is already in pcache?| diff = lfs_min(diff, pcache->size - (off-pcache->off));| memcpy(data, &pcache->buffer[off-pcache->off], diff);|| data += diff;| off += diff;| size -= diff;| continue;| }| // pcache takes priority| diff = lfs_min(diff, pcache->off-off);| }|| // 3. 如果rcache中有缓存对应数据,则从rcache中读|-> if (block == rcache->block &&| off < rcache->off + rcache->size) {| if (off >= rcache->off) {| // is already in rcache?| diff = lfs_min(diff, rcache->size - (off-rcache->off));| memcpy(data, &rcache->buffer[off-rcache->off], diff);|| data += diff;| off += diff;| size -= diff;| continue;| }| // rcache takes priority| diff = lfs_min(diff, rcache->off-off);| }|| // 4. 如果未命中cache且size大于等于read_size,| // 则读取内容大小超过cache一次加载的大小,此时从块中读|-> if (size >= hint && off % lfs->cfg->read_size == 0 &&|size >= lfs->cfg->read_size) {|// bypass cache?|diff = lfs_aligndown(diff, lfs->cfg->read_size);|lfs->cfg->read(lfs->cfg, block, off, data, diff);||data += diff;|off += diff;|size -= diff;|continue;|}|| // 5. 如果未命中cache且size小于read_size,则将块数据加载到rcache|-> rcache->block = block;| rcache->off = lfs_aligndown(off, lfs->cfg->read_size);| rcache->size = lfs_min(| lfs_min(| lfs_alignup(off + hint, lfs->cfg->read_size),| lfs->cfg->block_size)| - rcache->off,| lfs->cfg->cache_size);| int err = lfs->cfg->read(lfs->cfg, rcache->block,| rcache->off, rcache->buffer, rcache->size);

(3)block device写操作

lfs_bd_prog的作用是将源数据写入到目标块中。但实际上没有立即将数据写入的目标块,而是先将数据复制到pcache中,等到flush操作时才将pcache中的数据写到块中:

littlefs原理分析#[五]文件读写-开源基础软件社区

相关函数:

lfs_bd_prog(lfs_t *lfs,| lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate,| lfs_block_t block, lfs_off_t off,| const void *buffer, lfs_size_t size) | // 1. 检查是否已写完,未写完则继续步骤,否则结束|-> while (size > 0) ...|| // 2. 如果pcache已准备好,则将数据复制到pcache中|-> if (block == pcache->block &&| off >= pcache->off &&| off < pcache->off + lfs->cfg->cache_size) {| // already fits in pcache?| lfs_size_t diff = lfs_min(size,| lfs->cfg->cache_size - (off-pcache->off));| memcpy(&pcache->buffer[off-pcache->off], data, diff);|| data += diff;| off += diff;| size -= diff;|| // 2.1 如果pcache已满,则进行flush |-> if (pcache->size == lfs->cfg->cache_size) {| // eagerly flush out pcache if we fill up| lfs_bd_flush(lfs, pcache, rcache, validate);| continue;| }|| // 3. 如果pcache未准备好,则准备pcache|-> pcache->block = block;| pcache->off = lfs_aligndown(off, lfs->cfg->prog_size);| pcache->size = 0;

总结

本文介绍了littlefs中的文件读写机制,到这里littlefs大部分的操作就都已经做了分析了。下一篇文章将会介绍littlefs中的磨损均衡相关策略。

​​想了解更多关于开源的内容,请访问:​​

​​51CTO开源基础软件社区​​

​​https://ost.51cto.com​​。

责任编辑:jianghua 来源:51CTO开源基础软件社区 littlefs文件读写

推荐系统

  • 电脑公司Ghost Win8.1 x32 精选纯净版2022年7月(免激活) ISO镜像高速下载

    电脑公司Ghost Win8.1 x32 精选纯净版2022年7月(免激活) ISO镜像高速下载

    语言:中文版系统大小:2.98GB系统类型:Win8

    电脑公司Ghost Win8.1x32位纯净版V2022年7月版本集成了自2022流行的各种硬件驱动,首次进入系统即全部硬件已安装完毕。电脑公司Ghost Win8.1x32位纯净版具有更安全、更稳定、更人性化等特点。集成最常用的装机软件,精心挑选的系统维护工具,加上绿茶独有

  • 微软Win11原版22H2下载_Win11GHOST 免 激活密钥 22H2正式版64位免费下载

    微软Win11原版22H2下载_Win11GHOST 免 激活密钥 22H2正式版64位免费下载

    语言:中文版系统大小:5.13GB系统类型:Win11

    微软Win11原版22H2下载_Win11GHOST 免 激活密钥 22H2正式版64位免费下载系统在家用办公上跑分表现都是非常优秀,完美的兼容各种硬件和软件,运行环境安全可靠稳定。Win11 64位 Office办公版(免费)优化  1、保留 Edge浏览器。  2、隐藏“操作中心”托盘图标。  3、保留常用组件(微软商店,计算器,图片查看器等)。  5、关闭天气资讯。 

  • Win11 21H2 官方正式版下载_Win11 21H2最新系统免激活下载

    Win11 21H2 官方正式版下载_Win11 21H2最新系统免激活下载

    语言:中文版系统大小:4.75GB系统类型:Win11

    Ghost Win11 21H2是微软在系统方面技术积累雄厚深耕多年,Ghost Win11 21H2系统在家用办公上跑分表现都是非常优秀,完美的兼容各种硬件和软件,运行环境安全可靠稳定。Ghost Win11 21H2是微软最新发布的KB5019961补丁升级而来的最新版的21H2系统,以Windows 11 21H2 22000 1219 专业版为基础进行优化,保持原汁原味,系统流畅稳定,保留常用组件

  • windows11中文版镜像 微软win11正式版简体中文GHOST ISO镜像64位系统下载

    windows11中文版镜像 微软win11正式版简体中文GHOST ISO镜像64位系统下载

    语言:中文版系统大小:5.31GB系统类型:Win11

    windows11中文版镜像 微软win11正式版简体中文GHOST ISO镜像64位系统下载,微软win11发布快大半年了,其中做了很多次补丁和修复一些BUG,比之前的版本有一些功能上的调整,目前已经升级到最新版本的镜像系统,并且优化了自动激活,永久使用。windows11中文版镜像国内镜像下载地址微软windows11正式版镜像 介绍:1、对函数算法进行了一定程度的简化和优化

  • 微软windows11正式版GHOST ISO镜像 win11下载 国内最新版渠道下载

    微软windows11正式版GHOST ISO镜像 win11下载 国内最新版渠道下载

    语言:中文版系统大小:5.31GB系统类型:Win11

    微软windows11正式版GHOST ISO镜像 win11下载 国内最新版渠道下载,微软2022年正式推出了win11系统,很多人迫不及待的要体验,本站提供了最新版的微软Windows11正式版系统下载,微软windows11正式版镜像 是一款功能超级强大的装机系统,是微软方面全新推出的装机系统,这款系统可以通过pe直接的完成安装,对此系统感兴趣,想要使用的用户们就快来下载

  • 微软windows11系统下载 微软原版 Ghost win11 X64 正式版ISO镜像文件

    微软windows11系统下载 微软原版 Ghost win11 X64 正式版ISO镜像文件

    语言:中文版系统大小:0MB系统类型:Win11

    微软Ghost win11 正式版镜像文件是一款由微软方面推出的优秀全新装机系统,这款系统的新功能非常多,用户们能够在这里体验到最富有人性化的设计等,且全新的柔软界面,看起来非常的舒服~微软Ghost win11 正式版镜像文件介绍:1、与各种硬件设备兼容。 更好地完成用户安装并有效地使用。2、稳定使用蓝屏,系统不再兼容,更能享受无缝的系统服务。3、为

  • 雨林木风Windows11专业版 Ghost Win11官方正式版 (22H2) 系统下载

    雨林木风Windows11专业版 Ghost Win11官方正式版 (22H2) 系统下载

    语言:中文版系统大小:4.75GB系统类型:

    雨林木风Windows11专业版 Ghost Win11官方正式版 (22H2) 系统下载在系统方面技术积累雄厚深耕多年,打造了国内重装系统行业的雨林木风品牌,其系统口碑得到许多人认可,积累了广大的用户群体,雨林木风是一款稳定流畅的系统,一直以来都以用户为中心,是由雨林木风团队推出的Windows11国内镜像版,基于国内用户的习惯,做了系统性能的优化,采用了新的系统

  • 雨林木风win7旗舰版系统下载 win7 32位旗舰版 GHOST 免激活镜像ISO

    雨林木风win7旗舰版系统下载 win7 32位旗舰版 GHOST 免激活镜像ISO

    语言:中文版系统大小:5.91GB系统类型:Win7

    雨林木风win7旗舰版系统下载 win7 32位旗舰版 GHOST 免激活镜像ISO在系统方面技术积累雄厚深耕多年,加固了系统安全策略,雨林木风win7旗舰版系统在家用办公上跑分表现都是非常优秀,完美的兼容各种硬件和软件,运行环境安全可靠稳定。win7 32位旗舰装机版 v2019 05能够帮助用户们进行系统的一键安装、快速装机等,系统中的内容全面,能够为广大用户