虚拟磁盘那么多,怎么找到想要的?

信息的局部性:将虚拟磁盘 (文件) 组织成层次结构

利用信息的局部性组织虚拟磁盘

目录树

  • 逻辑相关的数据存放在相近的目录
.
└── 学习资料
    ├── .学习资料(隐藏)
    ├── 问题求解1
    ├── 问题求解2
    ├── 问题求解3
    ├── 问题求解4
    └── 操作系统

文件系统的 “根”

树总得有个根结点

  • Windows: 每个设备 (驱动器) 是一棵树
    • C:\ “C 盘根目录”
      • C:\Program Files\, C:\Windows, C:\Users, ...
    • 优盘分配给新的盘符
      • 为什么没有 A:\, B:\?
      • 简单、粗暴、方便,但 game.iso 一度非常麻烦……
  • UNIX/Linux
    • 只有一个根 /
      • 第二个设备呢?
      • 优盘呢???

目录树的拼接

UNIX: 允许任意目录 “挂载 (mount)” 一个设备代表的目录树

  • 非常灵活的设计
    • 可以把设备挂载到任何想要的位置
    • Linux 安装时的 “mount point”
      • /, /home, /var 可以是独立的磁盘设备

mount 系统调用

int mount(const char *source, const char *target,
          const char *filesystemtype, unsigned long mountflags,
          const void *data);
  • mount /dev/sdb /mnt (RTFM)
    • Linux mount 工具能自动检测文件系统 (busybox 不能)

真正的 Linux 启动流程

Linux-minimal 运行在 “initramfs” 模式

  • Initial RAM file system
  • 完整的文件系统
    • 可以包含设备驱动等任何文件
    • 但不具有 “持久化” 的能力

最小 “真正” Linux 的启动流程

export PATH=/bin
busybox mknod /dev/sda b 8 0
busybox mkdir -p /newroot
busybox mount -t ext2 /dev/sda /newroot
exec busybox switch_root /newroot/ /etc/init

通过 pivot_root (2) 实现根文件系统的切换

文件的挂载

文件的挂载引入了一个微妙的循环

  • 文件 = 磁盘上的虚拟磁盘
  • 挂载文件 = 在虚拟磁盘上虚拟出的虚拟磁盘 🤔

Linux 的处理方式

  • 创建一个 loopback (回环) 设备
    • 设备驱动把设备的 read/write 翻译成文件的 read/write
  • 观察 disk-img.tar.gz 的挂载
    • lsblk 查看系统中的 block devices (strace)
    • strace 观察挂载的流程
      • ioctl(3, LOOP_CTL_GET_FREE)
      • ioctl(4, LOOP_SET_FD, 3)

Filesystem Hierarchy Standard (FHS)

FHS enables software and user to predict the location of installed files and directories.

例子:macOS 是 UNIX 的内核 (BSD), 但不遵循 Linux FHS

目录管理:创建/删除/遍历

这个简单

  • mkdir
    • 创建一个目录
    • 可以设置访问权限
  • rmdir
    • 删除一个空目录
    • 没有 “递归删除” 的系统调用
      • (应用层能实现的,就不要在操作系统层实现)
      • rm -rf 会遍历目录,逐个删除 (试试 strace)
  • getdents
    • 返回 count 个目录项 (ls, find, tree 都使用这个)
      • 以点开头的目录会被系统调用返回,只是 ls 没有显示

更人类友好的目录访问方式

合适的 API + 合适的编程语言

from pathlib import Path

for f in Path('/proc').glob('*/status'):
    print(f.parts[-2], \
        (f.parent / 'cmdline').read_text() or '[kernel]')

硬 (hard) 链接

需求:系统中可能有同一个运行库的多个版本

  • libc-2.27.so, libc-2.26.so, ...
  • 还需要一个 “当前版本的 libc”
    • 程序需要链接 “libc.so.6”,能否避免文件的一份拷贝?

硬连接:允许一个文件被多个目录引用

  • 目录中仅存储指向文件数据的指针
  • 链接目录 ❌
  • 跨文件系统 ❌

大部分 UNIX 文件系统所有文件都是硬连接 (ls -i 查看)

  • 删除的系统调用称为 “unlink” (引用计数)

软 (symbolic) 链接

软链接:在文件里存储一个 “跳转提示”

  • 软链接也是一个文件
    • 当引用这个文件时,去找另一个文件
    • 另一个文件的绝对/相对路径以文本形式存储在文件里
    • 可以跨文件系统、可以链接目录、……
  • 类似 “快捷方式”
    • 链接指向的位置当前不存在也没关系
    • ~/usb/media/jyy-usb
    • ~/Desktop/mnt/c/Users/jyy/Desktop (WSL)

ln -s 创建软链接

  • symlink 系统调用

软链接带来的麻烦

“任意链接” 允许创建任意有向图 😂

  • 允许多次间接链接
    • a → b → c (递归解析)
  • 可以创建软连接的硬链接 (因为软链接也是文件)
    • ls -i 可以看到
  • 允许成环
    • find -L A | tr -d '/'
    • 可以做成一个 “迷宫游戏”
      • ssh 进入游戏,进入名为 end 的目录胜利
      • 只允许 ls (-i), cd, pwd
    • 所有处理符号链接的程序 (tree, find, ...) 都要考虑递归的情况