为什么挂载发生在现有目录上?

为什么挂载发生在现有目录上?

需要一个现有目录作为挂载点

$ ls
$ sudo mount /dev/sdb2 ./datadisk
mount: mount point ./datadisk does not exist
$ mkdir datadisk
$ sudo mount /dev/sdb2 ./datadisk
$

我发现它很混乱,因为它覆盖了目录的现有内容。挂载点目录有​​两个可能的内容,可能会意外切换(对于不执行挂载的用户)。

为什么不会mount发生在新创建的目录中?这就是图形操作系统显示可移动媒体的方式。很清楚该目录是否已挂载(存在)或未挂载(不存在)。我很确定有一个很好的理由,但我还没有发现它。

答案1

这是一个实施细节泄露的案例。

在 UNIX 系统中,每个目录都包含映射到的名称列表索引节点数字。索引节点保存元数据,告诉系统它是否是文件、目录、特殊设备、命名管道等。如果它是文件或目录,它还告诉系统在磁盘上哪里可以找到文件或目录内容。大多数索引节点是文件或目录。选项-ils列出 inode 编号。

挂载文件系统需要一个目录 inode,并在内核的内存副本上设置一个标志,表示“实际上,当查找该目录的内容时,请查看另一个文件系统”(请参阅​​幻灯片 10)这个演示文稿)。这相对容易,因为它更改单个数据项。

为什么它不为您创建一个指向新索引节点的目录条目?有两种方法可以实现这一点,但这两种方法都有缺点。一种是将新目录物理写入文件系统 - 但如果文件系统是只读的,则会失败!另一种方法是向每个目录列表进程添加一个实际上并不存在的“额外”内容的列表。这是很繁琐的,并且可能会对每个文件操作造成轻微的性能影响。

如果您想要动态创建的挂载点,系统automount可以做到这一点。特殊的非磁盘文件系统还可以随意创建目录,例如procsysdevfs等等。

编辑:另请参阅答案当您“装载”包含内容的现有文件夹时会发生什么?

答案2

如果mount(2) 必需的创建一个新目录作为挂载点,您无法在只读文件系统下挂载任何内容。那太愚蠢了,所以我们可以排除这种可能性。

如果安装可选地创建一个新目录作为挂载点,这会很奇怪。挂载/卸载并不总是发生,因此在内核中放置额外的逻辑来通过单个系统调用来完成这两个步骤不会带来重要的加速。mkdir(2)如果需要的话,只需将其留给用户空间来进行系统调用即可。德米特里的回答指出,做mount(2)这两件事就会使其成为非原子的。并且您需要一个额外的参数来mount(2)使用模式标志,例如open(2)take、for O_CREATO_EXCL等。与让用户空间执行此操作相比,这将是愚蠢的。

或者也许您问是否让mount(8)(进行系统调用的传统程序mount(2))执行此操作?这是可能的,但已经有一个非常适合mkdir(1)这项工作的工具,并且 Unix 的设计就是关于可以组合的良好小工具。如果您想要一个兼具这两种功能的工具,则可以轻松编写 shell 脚本来使用两个更简单的工具构建该工具。 (或者,正如 muru 评论的那样,udisksctl已经这样做了,所以您不必编写它。)此外,mount(8)util-linux 中的 Linux 正常支持mount -o x-mount.mkdir[=mode]使用其x-用户空间选项的语法,而不是传递到文件系统的选项。


现在更有趣的问题是:为什么父文件系统上必须有一个目录?

就像 pjc50 的回答指出的那样(没有关系,即使他有我的姓名缩写!),让挂载点显示在目录列表中将需要对每个readdir().

让挂载点作为目录存在于包含它们的目录(在父 FS 上)中是一个很好的技巧。 readdir()根本不必注意到它是一个挂载点。那只会发生如果挂载点用作路径组件。当然,路径解析必须检查路径的每个目录组件的挂载表。

答案3

挂载到现有目录实际上会进行原子调用mount:它要么成功,要么失败,至少从用户的角度来看。如果mount必须自己创建挂载点,则会出现两个故障点,从而无法保证干净的回滚。想象一下以下场景:

  1. mount成功创建挂载点
  2. mount尝试将新文件系统挂载到该目录,但失败
  3. mount尝试删除挂载点,但失败

系统最终会产生失败的副作用mount

这是另一个:

  1. umount成功卸载文件系统
  2. umount尝试删除挂载点,但失败

现在,应该umount返回成功还是失败?

答案4

我也一直想知道这一点。

一个简单的包装器,例如:

#!/bin/sh
eval "mkdir -p \"\$$#\"" 
/bin/mount "$@"  

保存为可执行脚本,mount在路径中覆盖的目录中命名/bin,如果它让您太烦恼的话,应该注意这一点

(在运行实际的mount二进制文件之前,它会创建一个以 的最后一个参数命名的目录mount,如果该目录尚不存在。)


或者,如果您不希望mount包装器创建目录的调用失败,您可以执行以下操作:

#!/bin/sh
set -e
eval "lastArg=\"\$$#\""
test -d "$lastArg" || { mkdir "$lastArg"; madeDir=1; }
/bin/mount "$@"  ||  {  test -z "$madeDir" || rmdir "$lastArg"; }

相关内容