跳至主要內容

如何让路径依赖的程序正常工作

NixOS-CN大约 4 分钟

如何让路径依赖的程序正常工作

一切皆是文件

Linux 沿用了 Unix 的“一切皆是文件”理念。所有的输入/输出设备,包括硬盘、终端、打印机等,都被抽象为文件。

而虚拟文件系统(VFS)是这种理念的一个重要实现。VFS 提供了一个抽象层,使得用户可以使用统一的接口来访问各种不同类型的文件系统。在 Linux 中,VFS 不仅用于访问真实的文件系统(如 ext4、btrfs 等),还可以用于访问各种虚拟文件系统(procfs、sysfs 等)。

当你想操作它们的时候,只需要使用统一的文件操作 API(如 open、read、write 等)。这种设计简约且易用,广受赞誉。

一种文件组织规范

我们在上面提到了 Linux 将一切抽象成文件,但是又如何组织这些文件呢?于是有了 FHS(Filesystem Hierarchy Standard)来指导统一的文件组织方式。

  • /bin 基本用户指令,无需特殊权限
  • /boot 引导文件
  • /dev 设备文件
  • /etc 系统配置
  • /home 用户家目录
  • /lib 基本共享库与内核模块
  • /media 可移动媒体的挂载点
  • /mnt 临时文件系统挂载点
  • /opt 拓展或业务软件
  • /run 运行时变量数据
  • /root 超级用户的家目录
  • /sbin 系统库
  • /srv 为系统服务准备的数据
  • /tmp 临时文件

FHS

我只罗列个大概,你可以前往这个网站open in new window了解更多有关信息。

FHS 规范了一种单根树形的文件组织方式,让不同发行版之间的程序移植变得可行,应用程序总是能找到它们期望的库文件或可执行程序。

不知道你还记得吗?在上一节我们提到过 Nix 会将包文件释放在一个不重复的哈希路径里,而不是诸如 /usr/bin 之类的路径,这是妥妥的放弃了 FHS 了,它该如何保证那些有路径依赖的软件顺利运行呢?

路径依赖

“路径依赖”是指应用程序在运行时需要访问的文件或目录的具体位置。例如,一个应用可能需要访问 /usr/lib 目录下的某个库文件,或者需要读取 /etc 目录下的配置文件。这些路径通常在应用的源代码中被硬编码,因此应用对它们有所依赖。

兼容办法

NixOS 会通过以下方式确保有路径依赖的软件正常运行:

  • 包装脚本(Wrapper Script):NixOS 会为一些应用生成包装脚本。在执行它们的时候,包装脚本会很自然的将环境变量(比如 LD_LIBRARY_PATH)传递进去(新进程会继承其父进程的环境),它们就能在被安排好的环境变量里找到自己的依赖库。

    常见环境变量

    PATH 用于补充可执行程序的路径,让你在任何地方都能直接输入它们的名字以调用它们。

    LIBRARY_PATH 用于在编译链接阶段查找动态链接库(*.so)和静态链接库(*.a)。这个环境变量只在编译链接阶段起作用,对运行时的库搜索路径有影响的是下一个环境变量。

    LD_LIBRARY_PATH 用于指定动态链接器(ld)查找可执行文件运行时所依赖的动态链接库(*.so)的路径。即用于在程序运行期间查找动态链接库时,指定除标准路径 /usr/lib 之外的路径。

  • 构建过程中的路径替换:Nix 能接触到构建软件这一环节,自然可以对硬编码的路径进行替换。

  • 符号链接:NixOS 会将常见的目录或文件链接到存储在 /nix/store 中的相应文件。因为根目录下面的组织几乎全是链接在实现,所以根目录下面文件的组织对 NixOS 来说并不是太重要。

可切换的系统状态

在 NixOS 中还有一个代(generation)的概念,我们将不同的配置文件生成的系统状态称之为代,这些代实际上是链接不同的构建结果构成的。我们在上面就已经说过根目录的组织对 NixOS 不是非常重要,因为每次生成代(系统状态)就会新建一个根,链接不同的构建结果,也就是说,这个根是可以随时由 NixOS 根据配置重建的。

在生成代以后,NixOS 会添加新的引导条目,指向不同代的根目录,这样就能引导不同的系统状态。当然也能使用命令行工具实时切换这些状态。