4. VxWorks dosFs文件系统
dosFs 是 VxWorks 中最常用的块设备文件系统,它直接兼容 PC 上的 FAT 文件系统,因此 U 盘、CF 卡、SD 卡、ATA 磁盘等介质只要按 FAT 格式化,就能被 VxWorks 直接挂载读写。理解 dosFs 的关键,是先看清 FAT 卷的磁盘布局,再理解挂载时驱动如何把块设备"翻译"成目录树,最后才是目录操作与文件读写的实现路径。
一、FAT12/16/32 的磁盘结构
一个 FAT 卷从第 0 扇区开始,按固定顺序排列若干区域:引导扇区(Boot Sector / BPB)、保留扇区、FAT 表区、根目录区(仅 FAT12/16)、数据区。引导扇区中的 BPB(BIOS Parameter Block)字段决定了后续所有区域的偏移和大小,dosFs 挂载时首先解析的就是它。
关键 BPB 字段包括:BPB_BytsPerSec(每扇区字节数,通常 512)、BPB_SecPerClus(每簇扇区数)、BPB_RsvdSecCnt(保留扇区数)、BPB_NumFATs(FAT 表份数,通常 2)、BPB_RootEntCnt(根目录项数,FAT32 为 0)、BPB_TotSec16/32(总扇区数)、BPB_FATSz16/32(每份 FAT 表扇区数)。FAT32 还额外使用 BPB_RootClus 指定根目录起始簇。
三种 FAT 类型的核心差异在于 FAT 表项宽度和根目录位置:FAT12 每项 12 位,最多寻址 4084 个簇,根目录固定;FAT16 每项 16 位,最多 65524 个簇,根目录固定;FAT32 每项 32 位(实际用低 28 位),簇号上限约 2.68 亿,根目录改为普通簇链,可动态增长。判断依据是簇总数:簇数 < 4085 为 FAT12,< 65525 为 FAT16,否则为 FAT32。
二、挂载流程:从块设备到目录树
dosFs 挂载的本质,是把一个块设备(如 /ata0、/sd0)与一个挂载点路径绑定,并读取 BPB 建立内存中的卷结构。典型调用是 dosFsVolFormat 格式化后,用 dosFsMount 或更常用的 dosFsMkfs/dosFsMount 组合完成挂载。实际工程中更常见的是通过 usrFdiskPartCreate 分区后,用 dosFsMount 挂载指定分区。
挂载过程可分为四步:第一步,打开块设备,读取第 0 扇区,校验签名 0x55AA;第二步,解析 BPB,计算 FAT 表起始扇区、根目录起始扇区、数据区起始扇区;第三步,读取 FAT 表首项,判断 FAT 类型(FAT12/16/32),并校验 BPB_TotSec 与设备实际容量是否一致;第四步,分配卷描述符,注册到 dosFs 的卷链表中,返回挂载点路径。若任一步失败,dosFsMount 返回 NULL,可用 errno 判断具体原因,如 S_dosFsLib_UNSUPPORTED 表示 FAT 类型不支持,S_dosFsLib_INVALID_BPB 表示 BPB 非法。
/* 典型挂载示例:将 ATA 磁盘 0 的 0 号分区挂载到 /fat0 */
STATUS st;
st = dosFsMount("/ata0", "/fat0", 0, 0);
if (st != OK) {
printf("mount failed, errno = %d\n", errno);
} else {
printf("mounted at /fat0\n");
}
挂载成功后,/fat0 就成为 dosFs 卷的根目录,后续所有 open、read、write、mkdir 等操作都通过 VxWorks 的 IO 系统转发到 dosFs 驱动。注意:dosFs 挂载点必须是一个空目录,且同一块设备不能被重复挂载到不同路径。
三、目录操作实现
dosFs 的目录操作遵循 VxWorks 标准 IO 接口,但底层实现与 FAT 目录项紧密相关。创建目录用 mkdir,删除空目录用 rmdir,切换目录用 chdir,获取当前目录用 getcwd。目录项在磁盘上占 32 字节,包含 8.3 短文件名、属性字节、起始簇号、文件大小等字段。长文件名(LFN)通过多个目录项拼接实现,dosFs 支持 LFN 读写。
目录遍历通常用 opendir/readdir/closedir 组合。例如列出 /fat0 下所有文件:
DIR *dir = opendir("/fat0");
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
printf("%s\n", ent->d_name);
}
closedir(dir);
需要注意,readdir 返回的 d_name 是当前目录项的名字,不包含路径;若要获取完整路径,需自行拼接。删除目录前必须确保目录为空,否则 rmdir 返回 ERROR。
四、文件读写实现
文件读写是 dosFs 最核心的功能。打开文件用 open,返回文件描述符;读用 read,写用 write,定位用 lseek,关闭用 close。dosFs 内部维护文件描述符表,每个描述符记录当前读写偏移、文件起始簇、当前簇号、簇内偏移等状态。
写文件时,dosFs 按簇分配空间:若当前簇写满,则从 FAT 表中查找空闲簇,将其链接到当前簇链尾部,并更新 FAT 表。若文件需要扩展但磁盘无空闲簇,write 返回 -1,errno 为 ENOSPC。读文件时,dosFs 根据偏移计算簇号和簇内偏移,从数据区读取对应扇区,若跨簇则沿 FAT 链跳转到下一簇。
/* 文件读写示例:写入并回读 */
int fd = open("/fat0/test.txt", O_CREAT | O_RDWR, 0666);
if (fd < 0) { printf("open fail\n"); return; }
char *msg = "VxWorks dosFs test";
write(fd, msg, strlen(msg));
lseek(fd, 0, SEEK_SET); /* 回到文件头 */
char buf[64] = {0};
int n = read(fd, buf, sizeof(buf) - 1);
printf("read %d bytes: %s\n", n, buf);
close(fd);
实际调试中,常见问题包括:写入后未 close 导致 FAT 表未同步、lseek 偏移越界、跨簇读写时簇链断裂等。建议每次写操作后调用 close 或 fsync 确保数据落盘;若需检查文件系统一致性,可用 dosFsCheck 工具扫描 FAT 表和目录项。
dosFsVolFormat 格式化,或确认介质已在 PC 上格式化为 FAT16/FAT32。挂载点目录必须存在且为空,否则挂载失败。读写大文件时,建议按簇大小(通常 4KB~32KB)对齐操作,减少 FAT 表跳转次数。
dosFsCheck 修复,或改用带日志的文件系统(如 TrueFFS、HRFS)。
总结来说,dosFs 的 FAT12/16/32 结构决定了磁盘寻址方式,挂载流程完成块设备到目录树的映射,目录操作和文件读写则通过标准 IO 接口调用底层 FAT 簇链管理。掌握这三层关系,就能在 VxWorks 中稳定地使用 U 盘、CF 卡等 FAT 介质进行数据存储与交换。