常见虚拟文件系统
binfs文件系统示例
参考文件:nuttx\fs\binfs\fs_binfs.c
内嵌命令与内嵌APP
openvela里含有“内嵌的命令”、“内嵌的APP”,所谓内嵌的命令就是在“apps\nshlib\nsh_command.c”中g_cmdmap数组里定义的“命令名、函数”:

所谓“内嵌APP”,是由“apps\examples\hello\hello_main.c”这样的多个程序组成的。“apps\examples”目录下多个APP被脚本生成为“apps\builtin\builtin_list.h”,内容如下(它们就是一个个builtin_s结构体):

builtin_list.h又被builtin_list.c包含,构造出了g_builtins数组,它就表示了所有的“内嵌APP”:

“apps/builtin/builtin_list.h”文件内容如下:
要想知道系统里有哪些内嵌的APP,可以通过挂载binfs文件系统来查看,比如执行:
mount -t binfs none /bin
ls /bin
binfs文件系统被用来“展示”openvela里内嵌的APP,是否挂载binfs对于系统没有影响。
另外:所有的“内嵌命令”和“内嵌APP”,可以使用“help”命令显示出来。
binfs文件系统解析
binfs文件系统的核心在于:在“g_builtins[]”数组里根据index或者名字找到对应的项:

- APP调用open函数传入path时,binfs_open遍历数组找到名字匹配的项:
index = builtin_isavail(relpath);
filep-\>f_priv = (FAR void \*)((uintptr_t)index);
- APP调用opendir时,binfs_opendir只能打开binfs的根目录,设置index为0
bdir = fs_heap_zalloc(sizeof(\*bdir));
bdir-\>index = 0;
- APP调用readdir时,binfs_readdir根据index获得对应内嵌APP的名字,累加index
bdir = (FAR struct binfs_dir_s \*)dir;
index = bdir-\>index;
name = builtin_getname(index);
entry-\>d_type = DTYPE_FILE;
strlcpy(entry-\>d_name, name, sizeof(entry-\>d_name));
index++;
bdir-\>index = index;
- APP调用rewinddir时,binfs_rewinddir里把index复位为0
((FAR struct binfs_dir_s \*)dir)-\>index = 0;
上机实验
配置项:
CONFIG_FS_BINFS=y
执行如下命令:
nsh> mount -t binfs /bin
nsh> ls /bin
/bin:
disp
fb
ft5x06
getprop
i2c
lvgldemo
mikey
nsh
rb
sb
setlogmask
setprop
sh
showlogo
procfs文件系统
参考文件:nuttx\fs\procfs\fs_procfs.c
特性与功能
在 openvela系统中,procfs(Process File System,进程文件系统)是一种特殊的伪文件系统,其设计理念借鉴了 Unix/Linux 中的“/proc”文件系统,主要用于“以文件接口的形式暴露系统内核和进程的实时信息”,是用户态与内核态交互的重要桥梁。
. 虚拟文件接口
procfs 中的“文件”并非实际存储在物理设备上,而是由内核在运行时动态生成。当用户态程序读取这些文件时,内核会实时收集并返回相关信息(如进程状态、内存使用、系统配置等);写入某些文件则可触发内核行为(如修改进程优先级)。
. 系统与进程信息暴露
主要提供两类信息:
-
系统级信息:如 CPU 架构、内存总量、内核版本、已挂载文件系统列表等,通常通过 “/proc/cpuinfo”、“/proc/meminfo”、“/proc/filesystems” 等虚拟文件访问。
-
进程级信息:每个运行中的进程在 “/proc” 下会有一个以进程 ID(PID)命名的子目录(如 “/proc/123”),包含该进程的状态(“status”)、内存映射(“maps”)、命令行参数(“cmdline”)等细节。
. 轻量化设计
作为嵌入式系统的组件,openvela的 procfs 针对资源受限场景进行了精简,仅保留核心功能(如必要的进程和系统信息接口),避免冗余开销,符合 openvela对内存和计算资源的严格控制要求。
(4)调试与监控工具基础
基于 procfs 提供的接口,开发者可以便捷地实现系统监控工具(如查看进程列表、资源占用)或调试工具(如跟踪进程状态变化),无需直接操作内核接口,降低了开发复杂度。
(5) 与其他文件系统的区别
-
不同于 BDFS(块设备文件系统)或 MDFS(内存设备文件系统),procfs 不依赖物理存储或内存块设备,完全是内核通过回调函数动态响应文件操作。
-
作为”伪文件系统”,其主要作用是信息交互而非数据存储,因此通常以只读方式挂载(部分文件支持写入以配置系统)。
总之,procfs 是 openvela中实现“通过文件系统接口访问系统内核信息”的关键组件,为嵌入式系统的调试、监控和管理提供了标准化、轻量化的解决方案。
procfs文件系统解析
procfs文件系统中的内容来自2方面:进程相关的文件存放在“/proc/<pid>”目录下,其他文件(比如/proc/cpufre、/proc/fs/blocks)等。

tmpfs文件系统示例
参考文件:nuttx\fs\tmpfs\fs_tmpfs.c
特性与功能
tmpfs(Temporary File System)是一种基于内存的文件系统:
-
基本概念:tmpfs中的数据存储在RAM中,不写入磁盘,这使得它具有快速的读写速度,非常适合存储临时文件。不过,系统重启后数据会丢失。tmpfs的大小可以动态调整,其最大限制为系统可用内存。
-
设计架构:openvela采用VFS(Virtual File System)架构,tmpfs是其具体文件系统实现之一。
-
实现原理:tmpfs基于内存管理子系统,tmpfs不需要先格式化就能使用,可以直接挂载,在写入文件时即时分配内存
-
应用场景:tmpfs通常用于临时文件存储,如/tmp、/var/tmp等临时目录,以及/run、/var/run等运行时目录。此外,对于需要快速读写文件但不要求持久保存的场景,tmpfs也能提供很高的性能,同时还可以减少对磁盘的读写操作,保护磁盘寿命。
tmpfs的结构体如下:
const struct mountpt_operations g_tmpfs_operations =
{
tmpfs_open, /* open */
tmpfs_close, /* close */
tmpfs_read, /* read */
tmpfs_write, /* write */
tmpfs_seek, /* seek */
tmpfs_ioctl, /* ioctl */
tmpfs_mmap, /* mmap */
tmpfs_truncate, /* truncate */
……
tmpfs文件系统解析
在tmpfs文件系统中,open/read/write一个文件时,在内核里用一个“tmpfs_file_s”结构体来描述一个文件,里面有它的存储位置、大小、当前读写位置,如下:
struct tmpfs_file_s
{
/* 前几个字段必须与通用TMPFS对象的结构布局保持一致(继承自基础对象tmpfs_object_s) */
rmutex_t tfo_lock; /* 对象互斥锁,用于保护文件对象的并发访问安全,防止多线程操作冲突 */
size_t tfo_alloc; /\* 文件对象的分配内存大小 \*/
uint8_t tfo_type; /* 文件对象类型,参考枚举类型enum tmpfs_objtype_e */
uint8_t tfo_refs; /* 引用计数器,用于内存回收判断 */
FAR struct tmpfs_directory_s *tfo_parent; /* 指向父目录对象的指针 */
/* 剩余字段是文件对象独有的(区别于目录对象的特有属性) */
uint8_t tfo_flags; /* 文件标志位,参考TFO_FLAG_*系列宏定义(如是否只读、是否追加等文件状态标识) */
size_t tfo_size; /\* 文件的有效数据大小(非分配内存大小) \*/
FAR uint8_t *tfo_data; /* 文件数据的起始地址指针 */
};
当APP打开tmpfs文件系统里的文件时,会调用“tmpfs_open”,里面会为所打开的文件创建一个“tmpfs_file_s”结构体,并记录在“filep->priv”里,如下:
ret = tmpfs_find_file(fs, relpath, &tfo, NULL);
filep-\>f_priv = tfo;
当APP调用read函数读取tmpfs文件系统里的文件时,根据filep->f_priv所指向的tfo结构体得到文件所在内存位置,然后使用memcpy把内存的数据复制出来,如下:
startpos = filep-\>f_pos;
nread = buflen;
endpos = startpos + buflen;
if (endpos \> tfo-\>tfo_size)
{
endpos = tfo-\>tfo_size;
nread = endpos - startpos;
}
memcpy(buffer, &tfo-\>tfo_data\[startpos\], nread);
filep-\>f_pos += nread;
当APP调用write函数写tmpfs文件系统里的文件时,根据filep->f_priv所指向的tfo结构体得到文件所在内存位置,然后判断、扩大内存,最后使用memcpy把APP提供的数据写入内存,如下:
nwritten = buflen;
endpos = startpos + buflen;
if (endpos \> tfo-\>tfo_size)
{
/* 重新分配文件内存,以处理写入超出文件末尾的数据(即 “写超尾” 场景). */
ret = tmpfs_realloc_file(tfo, (size_t)endpos);
if (ret \< 0)
{
goto errout_with_lock;
}
}
memcpy(&tfo-\>tfo_data\[startpos\], buffer, nwritten);