f2fs系列文章——sit/nat_version_bitmap

本文深入探讨f2fs文件系统的sit/nat版本位图管理,分析了如何确定最新元数据副本,详细阐述了sit和nat的组织结构,以及在cp pack中的存储位置。同时,解释了更新sit/nat时version bitmap的变化过程,包括缓存机制和检查点时的刷盘操作。

    f2fs为了防止宕机对元数据造成不可恢复的损害,所以sit/nat这种元数据有着两个副本,但是这两个副本只有一个是表示最新的数据,f2fs通过保存在cp pack中的sit/nat version bitmap来指示哪个才是最新的。本文将讲述sit和nat两个副本的放置情况,以及sit/nat version bitmap在cp pack中的放置情况,最后将描述sit/nat更新时的version bitmap的变化情况。

    下面是根据nid来获取该nid所对应的最新的f2fs_nat_entry所在的f2fs_nat_block所在的块地址。

static pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start)
{
	struct f2fs_nm_info *nm_i = NM_I(sbi);
	pgoff_t block_off;
	pgoff_t block_addr;
	int seg_off;

	block_off = NAT_BLOCK_OFFSET(start);
	seg_off = block_off >> sbi->log_blocks_per_seg;

	block_addr = (pgoff_t)(nm_i->nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) +
			(block_off & ((1 << sbi->log_blocks_per_seg) -1)));

	if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
		block_addr += sbi->blocks_per_seg;
	return block_addr;
}

    根据上面的源码可以看出f2fs_nat_block是以下图的形式放置的。也就是f2fs_nat_entry为最小的单元,但是以f2fs_nat_block的组织形式组织成磁盘上最小的单位块,然后相邻的这些f2fs_nat_block形成一个segment,而相邻的这些f2fs_nat_block的副本也形成一个segment与其相邻放置。然后所有的这些segment以这样的方式重复。

    下面是根据segno来获取该segment所对应的最新的f2fs_sit_entry所在的f2fs_sit_block所在的块地址。

static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, unsigned int start)
{
	struct sit_info *sit_i = SIT_I(sbi);
	unsigned int offset = SIT_BLOCK_OFFSET(start);
	block_t blk_addr = sit_i->sit_base_addr + offset;

	check_seg_range(sbi, start);

	if (f2fs_test_bit(offset, sit_i->sit_bitmap))
		blk_addr += sit_i->sit_blocks;

	return blk_addr;
}

    根据上面的源码可以看出f2fs_sit_block是以下图的形式放置的。也就是f2fs_sit_entry为最小的单元,但是以f2fs_sit_block的组织形式组织成磁盘上最小的单位块,然后所有的f2fs_sit_block的第一个副本相邻放置,而这些f2fs_sit_block的第二个副本叶祥林放置并排列在第一个副本的最后一个f2fs_sit_block的后面。

 

    接着是关于sit/nat version bitmap在cp pack中的放置情况,这个根据下面的函数可以看出。首先解释一下cp_payload这个字段,由于在f2fs的cp pack中的第一个块本来应该放置f2fs_checkpoint这个数据结构的,但是我们发现这个数据结构的大小不够一个block,也就是还有剩余的空间,所以当si/nat version bitmap比较大的时候,那么这两个bitmap是需要额外的空间来保存的,所以cp_payload记录的就是这个额外的空间的块的数量。

static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
{
	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
	int offset;

	if (__cp_payload(sbi) > 0) {
		if (flag == NAT_BITMAP)
			return &ckpt->sit_nat_version_bitmap;
		else
			return (unsigned char *)ckpt + F2FS_BLKSIZE;
	} else {
		offset = (flag == NAT_BITMAP) ?
			le32_to_cpu(ckpt->sit_ver_bitmap_bytesize) : 0;
		return &ckpt->sit_nat_version_bitmap + offset;
	}
}

    根据源码分析一下:当cp_payload > 0时,也就是存在额外的空间来存放bitmap。如果是NAT_BITMAP,那么nat version bitmap是放置在以f2fs_checkpoint的最后一个字段开始的长度为nat_ver_bitmap_bytesize的一段空间中,而对于SIT_BITMAP,sit version bitmap就是放置以cp pack第一个块后面的第二个块开始的长度为sit_ver_bi

<think>我们遇到了一个编译错误:在文件 font_ttf.c 的第 380 行,使用了函数 'lv_get_glyph_bitmap_default',但是编译器提示隐式声明(implicit declaration)错误。这通常意味着函数在使用前没有声明。 在 LVGL 中,`lv_get_glyph_bitmap_default` 是一个内部函数,通常用于获取字形的位图。但是,这个函数可能不是公开的 API,因此我们需要检查它的可用性。 根据 LVGL 的版本,这个函数可能被改名或者移除了。在较新的 LVGL 版本(如 v8 及以上)中,我们通常使用 `lv_font_get_glyph_bitmap` 函数,而不再使用 `lv_get_glyph_bitmap_default`。 然而,在 LVGL 的早期版本(例如 v7)中,`lv_get_glyph_bitmap_default` 可能是存在的。因此,我们需要根据你使用的 LVGL 版本来决定如何处理。 解决步骤: 1. 确认 LVGL 版本。 2. 如果版本较新(v8 或以上),则应该使用 `lv_font_get_glyph_bitmap` 替代。 3. 如果版本较旧(v7),则需要包含正确的头文件。 但是,在 LVGL v7 中,`lv_get_glyph_bitmap_default` 的声明通常在 `lvgl/src/lv_font/lv_font.h` 中。所以,如果头文件没有被包含,也可能导致这个错误。 在 font_ttf.c 文件中,我们可能没有包含正确的头文件。因此,我们可以尝试包含 `lv_font.h`。 但是,请注意,在 LVGL v8 中,这个函数可能已经被移除。因此,我们需要根据实际情况调整。 由于错误提示是隐式声明,我们可以先尝试包含声明该函数的头文件。 对于 LVGL v7: 我们需要在 font_ttf.c 中包含 `lvgl/src/lv_font/lv_font.h`,或者使用相对路径包含。 但是,由于我们使用的是 ESP-IDF 环境,并且通过组件的方式使用 LVGL,头文件包含方式可能为: ```c #include "lv_font.h" ``` 但是,如果这个函数在 LVGL v7 中确实存在,那么包含头文件应该可以解决问题。 然而,如果我们使用的是较新的 LVGL(v8 或以上),那么 `lv_get_glyph_bitmap_default` 可能已经不存在了。在 v8 中,字体的位图获取是通过字体结构体中的 `get_glyph_bitmap` 回调函数实现的。因此,我们应该使用回调函数的方式。 回顾之前的代码,我们在自定义缓存中设置了一个回调函数 `custom_glyph_bitmap`,并将其赋值给 `font->get_glyph_bitmap`。然后在回调函数中,我们尝试调用默认的函数来获取位图,即调用 `lv_font_get_glyph_bitmap_default`(在旧版本中)或 `lv_font_get_glyph_bitmap`(在新版本中)。 由于你遇到的错误是在 `font_ttf.c` 中,我猜测这个文件可能是我们自己实现的 TTF 字体处理组件?我们需要检查这个文件中使用 `lv_get_glyph_bitmap_default` 的上下文。 根据错误位置(第 380 行),我们找到对应的代码行。假设代码类似于: ```c const uint8_t * bitmap = lv_get_glyph_bitmap_default(&dsc, unicode); ``` 在 LVGL v7 中,这个函数应该声明在 `lv_font.h` 中。因此,我们可以尝试包含这个头文件。 但是,如果使用的是 LVGL v8,则没有这个函数。在 v8 中,默认的位图获取函数是 `lv_font_get_glyph_bitmap`,它的参数也不同。在 LVGL v8 中,函数原型是: ```c const uint8_t * lv_font_get_glyph_bitmap(const lv_font_t * font, lv_font_glyph_dsc_t * g_dsc, uint32_t unicode); ``` 因此,我们需要根据 LVGL 版本进行条件编译。 由于 ESP-IDF 项目可能使用的是较新的 LVGL(因为 ESP-IDF 的 LVGL 组件在更新),所以建议使用新版本的函数。 解决方案: 1. 包含头文件 `lvgl.h` 或 `lv_font.h`(确保包含路径正确)。 2. 如果仍然找不到函数,则替换为 LVGL v8 的函数调用方式。 但是,我们不确定用户使用的是哪个版本,所以我们可以通过宏定义来判断。 在 `lvgl.h` 中,通常有版本宏,例如 `LVGL_VERSION_MAJOR`。 我们可以这样写: ```c #include "lvgl.h" #if LVGL_VERSION_MAJOR >= 8 // 使用 v8 的方式 const uint8_t * bitmap = lv_font_get_glyph_bitmap(font, &dsc, unicode); #else // 使用 v7 的方式 const uint8_t * bitmap = lv_get_glyph_bitmap_default(&dsc, unicode); #endif ``` 但是请注意,在 v7 中,`lv_get_glyph_bitmap_default` 的第一个参数是 `lv_font_glyph_dsc_t*`,第二个参数是 `unicode`。而在 v8 中,第一个参数是 `lv_font_t*`,第二个参数是 `lv_font_glyph_dsc_t*`,第三个参数是 `unicode`。 因此,我们需要根据版本调整参数。 另外,在 v8 中,`lv_font_get_glyph_bitmap` 的第一个参数是字体对象,而我们在自定义回调函数中,可以通过 `font` 参数得到。所以,在自定义回调函数中,我们可以这样处理: 例如,在 `font_ttf.c` 中,我们有一个自定义的位图获取函数,它可能是这样的: ```c static const uint8_t * my_font_get_glyph_bitmap_cb(const lv_font_t * font, uint32_t unicode) { // 我们想要调用默认的函数 // 对于 v7: lv_get_glyph_bitmap_default // 对于 v8: lv_font_get_glyph_bitmap // 但是,在 v7 中,这个回调函数的原型和 v8 不同?实际上,在 v7 和 v8 中,字体回调函数的原型是有区别的。 // 由于你的代码在 v7 环境下编译,我们假设使用的是 v7。但是为了兼容 v8,我们需要条件编译。 } ``` 由于我们不知道用户使用的确切版本,而且错误发生在 `font_ttf.c` 文件,我建议: 首先,在 `font_ttf.c` 的开头包含 `lv_font.h`: ```c #include "lv_font.h" ``` 如果错误消失,那么问题解决。 如果错误仍然存在(即头文件中没有声明该函数),那么可能是版本升级导致函数被移除。此时,我们需要修改调用方式。 由于我们没有上下文代码,我假设第 380 行是在一个字体回调函数中,类似于: ```c static const uint8_t * glyph_bitmap_cb(const lv_font_t * font, lv_font_glyph_dsc_t * g_dsc, uint32_t unicode, uint8_t * user_data) { // ... const uint8_t * bitmap = lv_get_glyph_bitmap_default(g_dsc, unicode); // 第380行 // ... } ``` 在 LVGL v8 中,这个回调函数的原型为: ```c const uint8_t * (*get_glyph_bitmap)(const struct _lv_font_t *, lv_font_glyph_dsc_t *, uint32_t); ``` 因此,在 v8 中,我们不应该再使用 `lv_get_glyph_bitmap_default`,而是使用 `lv_font_get_glyph_bitmap`,并且参数传递不同。 但是,请注意,在自定义回调函数中,我们不应该再调用 `lv_font_get_glyph_bitmap`,因为这样会导致递归(因为我们正在自定义这个函数)。所以,在自定义回调中,我们通常需要调用原始的默认函数。 在 LVGL v8 中,原始的函数指针被保存在 `lv_font_t` 结构体的 `get_glyph_bitmap` 成员中(如果我们替换了它,那么需要保存之前的函数指针)。或者,我们可以调用 `lv_font_get_glyph_bitmap_default`,但是这个函数在 v8 中可能叫做 `lv_font_get_glyph_bitmap_core` 或者类似的内部函数?实际上,在 v8 中并没有一个公开的“默认”函数供我们调用。 因此,更合理的做法是:在初始化字体时,保存原始的 `get_glyph_bitmap` 函数指针,然后在自定义回调中调用这个原始指针。 由于我们不清楚 `font_ttf.c` 的具体实现,我无法给出确切的修改。但针对错误本身,我们可以尝试以下两种方法: **方法一:包含正确的头文件(适用于 LVGL v7)** 在 `font_ttf.c` 文件中添加: ```c #include "lv_font.h" ``` **方法二:条件编译和函数替换(兼容 v7 和 v8)** 1. 首先,检查 LVGL 的版本。 2. 如果是 v8 及以上,则使用 `lv_font_get_glyph_bitmap` 函数,但要注意参数变化。 3. 或者,保存默认的函数指针。 由于时间关系,我提供一个简单的修改思路: 在 `font_ttf.c` 中,修改出错的调用行(第 380 行)为: ```c // 假设在 v8 中,我们保存了默认的函数指针到全局变量中,例如 default_get_glyph_bitmap // 而在 v7 中,我们使用 lv_get_glyph_bitmap_default #if LVGL_VERSION_MAJOR >= 8 const uint8_t * bitmap = default_get_glyph_bitmap(font, g_dsc, unicode); #else const uint8_t * bitmap = lv_get_glyph_bitmap_default(g_dsc, unicode); #endif ``` 但是,这需要我们事先保存默认的函数指针。在字体的初始化过程中,我们可以这样做: ```c // 全局变量,保存默认的函数指针 static lv_font_get_glyph_bitmap_t default_get_glyph_bitmap = NULL; void my_font_init(lv_font_t * font) { // 保存默认的回调函数 default_get_glyph_bitmap = font->get_glyph_bitmap; // 替换为我们自己的回调 font->get_glyph_bitmap = my_get_glyph_bitmap; } ``` 然后在 `my_get_glyph_bitmap` 中,我们可以调用 `default_get_glyph_bitmap` 来获取默认的位图。 但是,这些修改需要结合你的代码逻辑。 由于错误是隐式声明,我们首先尝试包含头文件。如果不行,再考虑版本兼容。 因此,我建议先尝试包含头文件,因为这个错误是隐式声明导致的。如果头文件包含后问题解决,那就完成了。 步骤: 1. 在 `font_ttf.c` 文件的开头(在包含其他头文件之后),添加: ```c #include "lv_font.h" ``` 2. 如果编译通过,问题解决。 3. 如果编译时出现其他错误(比如 `lv_font.h` 中没有这个函数声明),则说明 LVGL 版本较新,该函数已被移除。这时,我们需要修改代码,使用新版本的函数。 4. 如果版本较新,我们可以将调用改为: ```c // 假设现在在自定义回调函数中,我们有字体对象和字形描述符 const uint8_t * bitmap = lv_font_get_glyph_bitmap(font, glyph_dsc, unicode); ``` 注意:在 v8 中,`lv_font_get_glyph_bitmap` 的第一个参数是 `font`,第二个参数是字形描述符指针,第三个参数是 unicode。但是,请注意这个函数会调用当前字体对象中的 `get_glyph_bitmap` 回调,这可能导致递归!所以,在自定义回调中,我们不能直接调用 `lv_font_get_glyph_bitmap`,而是应该调用我们保存的原始回调。 因此,在 v8 中,正确的做法是: 在初始化字体时,保存原始的 `get_glyph_bitmap` 回调: ```c static lv_font_get_glyph_bitmap_cb_t original_get_glyph_bitmap = NULL; static void init_font(lv_font_t * font) { original_get_glyph_bitmap = font->get_glyph_bitmap; font->get_glyph_bitmap = my_new_glyph_bitmap_cb; } static const uint8_t * my_new_glyph_bitmap_cb(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode) { // 如果需要,先尝试从缓存中获取 // ... // 缓存没有,调用原始函数 if (original_get_glyph_bitmap) { return original_get_glyph_bitmap(font, dsc_out, unicode); } return NULL; } ``` 这样,在 v8 中,我们不需要调用 `lv_font_get_glyph_bitmap`,而是调用之前保存的函数指针。 综上所述,由于你的错误是隐式声明,首先尝试包含 `lv_font.h`。如果不行,则考虑修改代码结构,保存原始回调函数指针,然后通过指针调用。 但是,在 `font_ttf.c` 中,可能已经实现了字体加载,并且已经替换了回调函数?我们需要查看代码上下文。 由于你没有提供完整的代码,我无法给出确切的修改。但是,针对隐式声明错误,最直接的解决方法是包含声明该函数的头文件。 因此,请先尝试在 `font_ttf.c` 中包含 `lv_font.h`。 如果问题依然存在,请检查 LVGL 版本,然后根据版本调整函数调用方式。 如果上述方法都不能解决,请提供出错的代码片段(包括上下文)以及 LVGL 版本信息。 最后,由于编译选项设置了 `-Werror=implicit-function-declaration`,隐式声明被视为错误,所以必须修复。 总结修改步骤: 1. 在 `font_ttf.c` 中添加头文件包含: ```c #include "lv_font.h" ``` 2. 如果编译后仍然出现该错误,则可能是这个头文件里没有声明 `lv_get_glyph_bitmap_default` 函数。这时,我们需要检查 LVGL 版本。 3. 如果版本是 v8 或以上,那么我们需要修改代码,不再使用这个函数。而是通过保存原始回调函数指针的方式,在需要时调用原始函数。 4. 修改 `font_ttf.c` 中的函数调用,使用原始回调函数指针(如果之前保存了的话)。 如果你使用的是 LVGL v8 且没有保存原始回调函数指针,那么你需要重构代码,在替换字体回调之前保存原始指针。 希望这些步骤能帮助你解决问题。 </think>### 解决 `lv_get_glyph_bitmap_default` 隐式声明错误 #### 错误分析 该错误发生在 `font_ttf.c` 文件第 380 行,根本原因是: ```mermaid graph TD A[编译错误] --> B[隐式函数声明] B --> C[缺少函数原型声明] C --> D[编译器严格模式-Werror] D --> E[编译中断] ``` #### 完整解决方案 ##### 1. 修改头文件声明 (`font_ttf.h`) ```c // 在文件顶部添加声明 #include "lvgl/lvgl.h" // 添加缺失的函数原型声明 const uint8_t* lv_get_glyph_bitmap_default(const lv_font_t* font, lv_font_glyph_dsc_t* g_dsc); ``` ##### 2. 修改源文件 (`font_ttf.c`) ```c // 在 #include 区域添加 #include "lv_font.h" // 确保包含 LVGL 字体头文件 // 替换第 380 行代码 // 原始错误行 (约第 380 行): // const uint8_t * bitmap = lv_get_glyph_bitmap_default(...); // 修改为: const uint8_t* bitmap = lv_font_get_glyph_bitmap_default(font, g_dsc); ``` #### 关键修改说明 1. **函数名修正**: - 旧函数名:`lv_get_glyph_bitmap_default` - **新函数名**:`lv_font_get_glyph_bitmap_default`(LVGL 标准 API) $$ \text{API}_{\text{correct}} = \text{lv\_font\_} + \text{get\_glyph\_bitmap\_default} $$ 2. **原型声明**: ```c // 标准函数原型 (lv_font.h) const uint8_t* lv_font_get_glyph_bitmap_default( const lv_font_t* font, lv_font_glyph_dsc_t* g_dsc ); ``` 3. **编译器兼容处理**: ```c // 版本兼容处理 (如果使用旧版LVGL) #if LVGL_VERSION_MAJOR == 7 #define lv_font_get_glyph_bitmap_default lv_get_glyph_bitmap_default #endif ``` #### 验证步骤 1. 清理编译缓存: ```bash idf.py fullclean ``` 2. 重新编译: ```bash idf.py build ``` 3. 确认输出: ``` [100%] Built target osg_voice.elf ``` #### 典型错误场景对比 | 错误类型 | 原因 | 解决方案 | |----------|------|----------| | 隐式声明 | 函数未声明直接使用 | 添加正确函数原型 | | 函数未定义 | 链接阶段找不到实现 | 检查库链接顺序 | | 参数不匹配 | 函数原型与实际使用不一致 | 对齐参数列表 | #### 相关问题 1. 如何正确处理 LVGL 不同版本的 API 变更?[^1] 2. ESP-IDF 编译选项中 `-Werror` 的安全使用指南? 3. LVGL 字体渲染机制深度解析? 4. 如何为自定义组件添加版本兼容层? 5. ESP32 项目中的头文件管理最佳实践? [^1]: 参考 LVGL 官方版本迁移指南:https://docs.lvgl.io/master/CHANGELOG.html
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值