2. dosFs文件系统详解:dosFs的特点、适用场景、dosFs的创建与格式化
好,咱们进入第二个文件系统——dosFs。说实话,这是我在VxWorks项目里打交道最多的一个文件系统。为什么?因为它简单、可靠,而且跟Windows的FAT文件系统兼容。你想想看,很多时候我们需要把设备上的日志或者数据拷到PC上分析,dosFs格式的存储介质插到Windows上直接就能读,这多方便。
2.1 dosFs的特点
dosFs,说白了就是VxWorks对FAT文件系统的实现。它支持FAT12、FAT16和FAT32三种格式。我个人的习惯是,小容量存储用FAT12或FAT16,超过512MB的盘就用FAT32。
它的核心特点有这么几个:
- 兼容性好:跟Windows、Linux的FAT文件系统互通。我在项目中经常用这个特性,把设备上的CF卡拔下来插到笔记本上,直接就能读数据。
- 实现简单:代码量小,内存占用低。对于资源受限的嵌入式系统来说,这是个很大的优势。
- 支持长文件名:VxWorks的dosFs支持VFAT长文件名,不过我记得早期版本默认是关闭的,需要手动开启。
- 支持多种介质:从RAM Disk到Flash,从IDE硬盘到SD卡,基本上块设备都能用。
重要提醒:dosFs没有日志功能。这意味着如果突然掉电,正在写的文件可能会损坏。我在一个电力监控项目里就吃过这个亏——设备突然断电,结果配置文件写了一半,重启后系统直接起不来了。从那以后,关键数据我都不敢只用dosFs。
2.2 适用场景
根据我的经验,dosFs最适合用在下面这些场景:
- 数据交换场景:需要把数据拿到PC上分析。比如设备运行日志、采集的数据文件。
- 启动加载场景:从FAT格式的SD卡或U盘加载bootrom和VxWorks镜像。很多开发板就是这么干的。
- 小容量存储:几MB到几GB的存储设备,dosFs的额外开销很小。
- 非关键数据:可以容忍掉电丢失的数据,比如临时缓存文件。
但要注意,如果你需要高可靠性、掉电保护、大容量存储(超过32GB),那dosFs就不太合适了。这时候应该考虑后面要讲的rawFs或者TrueFFS。
2.3 dosFs的创建与格式化
好,咱们来点实际的。怎么在VxWorks里创建一个dosFs文件系统?
2.3.1 准备工作:先有一个块设备
在创建dosFs之前,你得先有一个块设备。比如一个RAM Disk,或者一个Flash分区。这里我用RAM Disk做演示,因为最简单:
/* 创建一个2MB的RAM Disk */
ramDevCreate(0, 512, 4096, 0)
/* 返回值是设备名,比如"/ram0" */
嗯,这里要注意参数的含义:第二个参数是块大小(512字节),第三个参数是块数量(4096块),总共就是2MB。
2.3.2 格式化设备
有了块设备,接下来就是格式化。dosFs的格式化用dosFsFormat函数:
/* 格式化RAM Disk为FAT16 */
dosFsFormat("/ram0", 0, 0, 0, 0, 0)
/* 参数说明:
* 第一个参数:设备名
* 后面几个参数:0表示使用默认值
*/
格式化完成后,设备上就有了FAT文件系统的结构——引导扇区、FAT表、根目录区、数据区。
小技巧:如果你想让dosFs支持长文件名,可以在格式化后调用dosFsVolOptSet来设置卷选项。我曾经在一个项目里忘了开这个,结果客户说文件名超过8个字符就报错,搞得我排查了半天。
2.3.3 挂载文件系统
格式化只是写入了文件系统的元数据,要真正使用,还得挂载:
/* 挂载dosFs文件系统 */
dosFsDevCreate("/ram0", 0, 0, 0, 0, 0)
/* 或者用更通用的方式 */
dosFsMkfs("/ram0", 0, 0, 0, 0, 0)
挂载成功后,你就可以用标准的文件操作函数了——open、read、write、close,跟操作普通文件一样。
2.3.4 完整示例
下面是一个完整的创建、格式化、挂载、读写示例:
#include <dosFsLib.h>
#include <ramDrv.h>
STATUS createDosFsExample(void)
{
/* 1. 创建RAM Disk */
char *devName = ramDevCreate(0, 512, 4096, 0);
if (devName == NULL) {
printf("RAM Disk创建失败\n");
return ERROR;
}
printf("RAM Disk创建成功: %s\n", devName);
/* 2. 格式化为FAT16 */
if (dosFsFormat(devName, 0, 0, 0, 0, 0) != OK) {
printf("格式化失败\n");
return ERROR;
}
printf("格式化成功\n");
/* 3. 挂载文件系统 */
if (dosFsDevCreate(devName, 0, 0, 0, 0, 0) != OK) {
printf("挂载失败\n");
return ERROR;
}
printf("挂载成功\n");
/* 4. 创建并写入文件 */
int fd = open("/ram0/test.txt", O_CREAT | O_RDWR, 0666);
if (fd == ERROR) {
printf("文件创建失败\n");
return ERROR;
}
write(fd, "Hello dosFs!", 12);
close(fd);
printf("文件写入成功\n");
return OK;
}
2.4 常见问题与避坑指南
最后,分享几个我在项目中遇到的dosFs问题:
- 文件名大小写:dosFs默认不区分大小写,但VFAT模式下可以保留大小写。我曾经因为这个问题,在脚本里用
Test.txt和test.txt搞混过。 - 簇大小选择:簇越大,大文件读写性能越好,但空间浪费也越多。对于小文件多的场景,建议用512字节或1KB的簇。
- 掉电保护:再次强调,dosFs没有日志。如果对可靠性要求高,可以考虑用dosFs的同步写模式(
O_SYNC标志),但性能会下降。
警告:不要在dosFs文件系统上频繁创建和删除小文件。FAT文件系统的碎片化问题很严重,时间长了性能会急剧下降。我曾经在一个数据采集设备上遇到过——跑了三个月后,写一个1KB的文件要等好几秒,就是因为碎片太多了。
好了,dosFs的内容就这些。它虽然简单,但用好了能解决很多实际问题。下一章咱们聊聊rawFs——那个更底层、更灵活的文件系统。