TMPFS 是一种在内存中创建临时文件系统的文件系统,通常用于 Linux 系统中。TMPFS 不会将数据存储在硬盘上,而是将其保存在系统的 RAM 中。因此,TMPFS 文件系统非常快速,适用于需要快速访问的临时数据,如临时文件、日志、进程间通信等。TMPFS 文件系统的一些主要特点和概述:

  • 存储在 RAM 中: TMPFS 文件系统将数据存储在系统的 RAM 中,因此数据的读取和写入速度非常快。这使得它适用于需要高性能临时存储的场景
  • 临时性质: TMPFS 文件系统通常用于存储临时数据,例如系统日志、进程间通信、临时文件和运行时文件。它不是用于永久数据存储的文件系统,因为数据会在系统重新启动时丢失
  • 动态分配内存: TMPFS 会动态分配系统 RAM 以存储数据,因此它的大小可以根据需要增长。这使得它非常适合处理不确定大小的临时数据
  • 可限制容量: 您可以配置TMPFS文件系统的最大容量,以防止它占用过多的系统内存。这有助于管理系统的内存资源
  • 透明的数据管理: TMPFS 通过将数据保存在内存中,使数据的读取和写入变得非常快速。同时它会将不再需要的数据清理出内存,以释放内存资源
  • 用途广泛: TMPFS 文件系统在 Linux 中用于多种用途,包括存储临时文件、临时目录、进程间通信(IPC)等。例如许多 Linux 发行版使用 TMPFS 来挂载 /tmp 目录,以提供快速的临时存储

由于 TMPFS 存储在 RAM 中,因此其容量受限于系统的可用内存。如果将大量数据写入 TMPFS,可能会导致系统内存不足的问题,因此需要谨慎使用。此外由于 TMPFS 是临时性的,不适用于需要永久数据存储的情况。如果需要持久性的数据存储,应该使用其他文件系统,如 EXT4、XFS 或 Btrfs.

图片无法显示,请右键点击新窗口打开图片

TMPFS 文件系统提供映射文件的 mmap 接口使用了 shmem_mmap 函数,shmem_mmap 函数为文件映射的 VMA 提供的 vm_ops 接口为 shmem_vm_ops,该数据接口实现了 fault 接口 shmem_fault,那么文件映射 VMA 发生缺页时 shmem_fault 函数会被调用.

图片无法显示,请右键点击新窗口打开图片

在 TMPFS 文件系统里,文件映射(File-Mapped) 的数据架构如上图,用户进程使用文件映射将文件映射到进程地址空间之后,进程使用 VMA 描述映射之后的虚拟内存区域,TMPFS 文件系统会为 VMA 提供相应 generic_file_vm_ops,另外 vm_file 指向映射的文件(struct file), 其又指向唯一的 STRUCT inode, 其 mapping 成员用于指向 STRUCT address_space, 该数据结构用于维护文件与 PAGE CACHE 和 VMA 的映射关系,其中 i_mmap 成员指向一颗区间树(RB-TREE), 该区间树维护了映射到该文件的 VMA. 另外 i_pages 指向 XARRAY 数组,该数组维护了文件映射的 PAGE CACHE,每个 PAGE CACHE 对应一个 STRUCT page 数据结构,STRUCT page 的 mapping 成员反过来指向 STRUCT address_space, 那么可以知道 PAGE CACHE 被哪些 VMA 映射,因此形成了一个闭环. 当进程首次访问 VMA 虚拟内存区域时,会触发缺页异常构造这些逻辑。那么接下来通过一个实践案例了解这种异常场景,实践案例在 BiscuitOS 上的部署逻辑如下:

cd BiscuitOS
make menuconfig

  [*] DIY BiscuitOS/Broiler Hardware  --->
      [*] Pseudo Filesystem: TMPFS
  [*] Package  --->
      [*] Paging Mechanism  --->
          [*] Page Fault with File-Mapped TMPFS --->

# 部署实践案例
make
# 源码目录
cd BiscuitOS/output/linux-6.0-x86_64/package/BiscuitOS-PAGING-PF-FILE-TMPFS-default/
# 部署源码
make download
# 在 BiscuitOS 中实践
make build

BiscuitOS-PAGING-PF-FILE-TMPFS-default Source Code on Gitee

图片无法显示,请右键点击新窗口打开图片

实践案例由一个应用程序构成,进程在 23 行在 “/mnt/tmpfs/” 目录下打开文件 BiscuitOS.txt 文件,该目录已经挂载为 TMPFS 文件系统,进程接着在 29 行调用 mmap 函数将文件映射到进程的地址空间,并在 40 行对文件对应的虚拟内存进行写操作,然后在 42 行对虚拟内存进行读操作,操作完毕之后就是释放虚拟内存和关闭文件. 以上便是一个最基础的实践案例,可以知道 40 行读操作就会触发缺页,为了可以看到内存在缺页异常里的流动,在 40 行前后加上 BS_DEBUG 开关:

图片无法显示,请右键点击新窗口打开图片 图片无法显示,请右键点击新窗口打开图片

接着在 TMPFS 文件映射内存缺页流程必经之路上任意位置加上 BS_DEBUG 函数,以此观察内存在某个函数里的流动,例如上图在 shmem_fault 函数的 2069 行加上 bs_debug 打印,以此确认内存流动到这里,接下来执行如下命令进行实践(需要提前打开内核宏: CONFIG_TMPFS_FS):

# 编译应用程序
cd BiscuitOS/output/linux-6.0-x86_64/package/BiscuitOS-PAGING-PF-FILE-TMPFS-default/
# 编译内核
make kernel
# 编译实践案例
make build

图片无法显示,请右键点击新窗口打开图片

当 BiscuitOS 启动之后,直接运行 RunBiscuitOS.sh 脚本,脚本里包括实践所需的命令,可以看到进程执行之后对虚拟内存的访问引起了缺页异常,并且该案例的缺页异常处理流程打印了字符串 “TMPFS PF on filemap_fault 0x6000000000”, 那么说明实践案例分配了 PAGECACHE,同时也可以看到 PAGECACHE 按着之前分析的代码路径流动。最后开发者可以在该路径上的任何地方使用 bs_debug 查看 PAGECACHE 在缺页异常处理流程里的流动.

图片无法显示,请右键点击新窗口打开图片

对于 TMPFS 文件系统映射文件到地址空间之后,进程访问该虚拟内存时,由于 MMU 发现对于的物理内存不存在,那么触发缺页异常。在缺页异常处理函数里,其主要做两个事情,首先是分配 PAGECACHE,如上图调用 shmem_alloc_and_acct_folio 函数进行分配; 第二个任务就是更新页表指向新的 PAGECACHE,以及更新页表标记为脏页等. 完成两个任务之后缺页异常处理函数就返回,那么进程可以正常访问虚拟内存.

图片无法显示,请右键点击新窗口打开图片

do_shared_fault 函数作为 TMPFS 文件系统映射内存写操作导致缺页的核心处理函数,函数调用 __do_fault 函数分配物理内存,其细节涉及 XARRY 等映射这里不放开讲。当分配物理内存之后,文件映射内存没有提供 VMA 对应的 page_mkwrite 接口,于是直接调用 finish_fault 函数进行页表设置,最后如果页变成脏页,则调用 fault_dirty_shared_page 函数进行标脏,以上便是可读可写共享内存的缺页过程。对于读操作导致的缺页,其核心调用 do_read_fault 函数,对于共享内存来说,其逻辑与 do_shared_fault 函数无异.

图片无法显示,请右键点击新窗口打开图片