外行人对“一切皆文件”的解释——与Windows有什么不同?

外行人对“一切皆文件”的解释——与Windows有什么不同?

我知道“一切都是文件”意味着即使是设备在 Unix 和类 Unix 系统中也有其文件名和路径,这使得通用工具可以在各种资源上使用,无论其性质如何。但我无法将其与我使用过的唯一其他操作系统 Windows 进行比较。我读过一些关于这个概念的文章,但我认为它们对于非开发人员来说有些难以理解。人们需要的是外行人的解释!

例如,当我想将文件复制到连接到读卡器的CF卡时,我将使用类似

zcat name_of_file > /dev/sdb

在Windows中,我认为读卡器将作为驱动程序出现,我想我们也会做类似的事情。那么,“一切皆文件”的理念在这里有何作用呢?

答案1

“一切都是文件”有点油嘴滑舌。 “一切都出现在某个地方文件系统” 更接近目标,即便如此,它也更像是一种理想,而不是系统设计的法则。

例如,Unix 域套接字不是文件,但它们确实出现在文件系统中。您可以使用ls -l域套接字来显示其属性,通过 修改其访问控制chmod,并且在某些 Unix 类型系统(例如 macOS,但不是 Linux)上,您甚至可以cat将数据传入/传出。

但是,尽管经常TCP/IP 网络套接字使用相同的方法创建和操作BSD 套接字系统调用如 Unix 域套接字、TCP/IP 套接字不是出现在文件系统中,尽管没有特别充分的理由表明这是真的。

文件系统中出现的非文件对象的另一个例子是Linux的/proc文件系统。此功能向用户公开了有关内核运行时操作的大量详细信息用户空间,主要作为虚拟纯文本文件。许多/proc条目是只读的,但很多/proc也是可写的,因此您可以使用任何可以修改文件的程序来更改系统运行的方式。唉,我们又遇到了一个非理想的情况:BSD Unixes/proc默认情况下不运行,并且 System V Unixes 暴露的 via 比 Linux 少得多/proc

我无法将其与 MS Windows 进行对比

首先,你可以在网上和书籍中找到关于 Unix 全部都是文件 I/O 以及 Windows 在这方面“损坏”的观点,这些观点已经过时了。视窗系统修复了很多这个问题。

现代版本的 Windows 具有统一的 I/O 系统,就像 Unix 一样,因此您可以通过以下方式从 TCP/IP 套接字读取网络数据ReadFile()而不是 Windows Sockets 特定的 APIWSARecv(), 如果你想。这恰好平行于Unix方式,您可以在其中使用通用的网络套接字读取read(2)Unix 系统调用或特定于套接字的recv(2)呼叫.²

尽管如此,即使到了 2021 年,Windows 仍然未能将这一概念提升到与 Unix 相同的水平。Windows 架构的许多区域无法通过文件系统访问,或者无法被视为类文件。一些例子:

  1. 司机。

    Windows 的驱动程序子系统很容易与 Unix 的驱动程序子系统一样丰富和强大,但是要编写程序来操作驱动程序,通常必须使用Windows 驱动程序套件,这意味着编写 C 或 .NET 代码。

    在 Unix 类型的操作系统上,您可以从命令行对驱动程序执行很多操作。只要将不需要的输出重定向到/dev/null.³ ,您几乎肯定已经做到了这一点

  2. 程序间通信。

    Windows 程序不像 Unix 命令行程序那样通过文本流和管道轻松地相互通信。 Unix GUI 通常要么构建在命令行程序之上,要么导出文本命令界面,因此相同的简单的基于文本的通信机制也适用于 GUI 程序。

  3. 注册表。

    Unix 没有与 Windows 注册表直接等效的功能。相同的信息分散在文件系统中,主要位于/etc/proc和中/sys

如果您没有看到驱动程序、管道和 Unix 对 Windows 注册表的回答与“一切都是文件”有任何关系,请继续阅读。

“一切都是文件”的理念在这里有何影响?

我将通过扩展以上三点来详细解释这一点。

长答案,第 1 部分:驱动器与设备文件

假设您的 CF 卡读卡器E:在 Windows 和/dev/sdcLinux 下显示。它有什么实际区别?

这不仅仅是一个微小的语法差异。

在 Linux 上,我可以说用零dd if=/dev/zero of=/dev/sdc覆盖 的内容/dev/sdc

想一想这意味着什么。这里我有一个普通的用户空间程序(dd(1)),我要求从虚拟设备( )读取数据,并通过统一的 Unix 文件系统将/dev/zero读取的数据写入真实的物理设备( )。不知道它正在读取和写入特殊设备。它也适用于常规文件,或者混合设备和文件,如下所示。/dev/sdcdd

在 Windows 上没有简单的方法将E:驱动器清零,因为 Windows 区分文件和驱动器,因此您不能使用相同的命令来操作它们。您可以获得的最接近的方法是在没有“快速格式化”选项的情况下进行磁盘格式化,该选项会将最多驱动器内容,但随后在其上写入新的文件系统。如果我不这样做怎么办新的文件系统?如果我真的希望磁盘上只填充零怎么办?

让我们慷慨地接受这个要求,在E:.要在 Windows 上的程序中执行此操作,我必须调用特殊的格式化 API。⁴ 在 Linux 上,您不需要编写程序来访问操作系统的“格式化磁盘”功能:您只需运行适当的用户空间程序即可您要创建的文件系统类型,无论是mkfs.ext4,mkfs.xfs,或者你有什么。这些程序会将文件系统写入/dev您传递的任何文件或节点上。

因为mkfsUnixy 系统上的类型程序不会人为区分设备和普通文件,所以我可以创建一个ext4 文件系统在我的 Linux 机器上的一个普通文件中:

$ dd if=/dev/zero of=myfs bs=1k count=1k
$ mkfs.ext4 -F myfs

myfs这将创建一个在当前目录中调用的 1 MiB 磁盘映像。然后我可以像安装任何其他外部文件系统一样安装它:

$ mkdir mountpoint
$ sudo mount -o loop myfs mountpoint
$ grep $USER /etc/passwd > mountpoint/my-passwd-entry
$ sudo umount mountpoint

现在我有一个 ext4 磁盘映像,其中包含一个名为的文件,my-passwd-entry其中包含我的用户/etc/passwd条目。

如果我愿意,我可以将该图像发送到我的 CF 卡上:

$ sudo dd if=myfs of=/dev/sdc1

或者,我可以将该磁盘映像打包,邮寄给您,然后让您将其写入介质你的选择,例如 USB 记忆棒:

$ gzip myfs
$ echo "Here's the disk image I promised to send you." | 
  mutt -a myfs.gz -s "Password file disk image" \
      [email protected]

所有这一切在 Linux⁵ 上都是可能的,因为文件、文件系统和设备之间没有人为的区别。 Unix 系统上的很多东西文件,或通过文件系统访问,以便它们看起来像文件,或以其他方式看起来足够像文件,因此可以将其视为文件。

Windows 的文件系统概念是一个大杂烩。它区分目录、驱动器和网络资源。存在三种不同的语法,在 Windows 中全部混合在一起:类 Unix..\FOO\BAR路径系统、驱动器号(如C:)和 UNC 路径(如\\SERVER\PATH\FILE.TXT。这是因为它是 Unix 思想的积累,CP/M,操作系统, 和局域网管理器而不是单一的连贯设计。这就是为什么有Windows 文件名中有这么多非法字符

Unix 有一个统一的文件系统,所有内容都可以通过一个通用方案访问。对于在 Linux 机器上运行的程序, /etc/passwd/media/CF_CARD/etc/passwd、 和之间没有功能差异/mnt/server/etc/passwd。本地文件、外部媒体和网络共享都以相同的方式处理。⁶

Windows 可以实现与上面的磁盘映像示例类似的目标,但您必须使用由非常有才华的程序员编写的特殊程序。这就是为什么有Windows 上有很多“虚拟 DVD”类型的程序。核心操作系统功能的缺乏为填补这一空白的程序创造了一个人造市场,这意味着有一群人竞相创建最好的虚拟 DVD 类型程序。我们在 *ix 系统上不需要这样的程序,因为我们可以使用循环装置

其他工具也是如此,例如磁盘擦除程序,我们在 Unix 系统上也不需要这些程序。想要您的 CF 卡的内容被不可挽回地打乱而不只是归零吗?好的,使用/dev/random作为数据源而不是/dev/zero

$ sudo dd if=/dev/random of=/dev/sdc

在 Linux 上,我们不会不断地重新发明这样的轮子,因为核心操作系统功能不仅运行得足够好,而且运行得非常好,以至于被广泛使用。几种方法之一启动 Linux 机器涉及到使用我上面展示的技术创建的虚拟磁盘映像。

我觉得公平地指出,如果 Unix 从一开始就将 TCP/IP I/O 集成到文件系统中,我们就不会有netcatvs socatvs ncatvs vs 的情况了。nc 混乱其原因与导致 Windows 上磁盘映像和擦除工具激增的设计缺陷相同:缺乏可接受的操作系统设施。

长答案,第 2 部分:管道作为虚拟文件

尽管 Windows 起源于 MS-DOS,但它从来没有丰富的命令行传统。

这并不是说 Windows 不命令行,或者它缺少许多命令行程序。如今,Windows 甚至有一个非常强大的命令 shell,恰当地称为电源外壳

然而,缺乏命令行传统会产生连锁反应。你会得到像这样的工具DISKPART这在 Windows 世界中几乎是未知的,因为大多数人通过计算机管理 MMC 管理单元进行磁盘分区等操作。然后,当您确实需要编写创建分区的脚本时,您会发现它DISKPART并不是真正由另一个程序驱动的。是的,您可以将一系列命令写入脚本文件并通过以下方式运行DISKPART /S scriptfile,但这是全有或全无的。你什么真的在这种情况下想要的更像是GNUparted,它将接受单个命令,例如parted /dev/sdb mklabel gpt.这允许您的脚本逐步进行错误处理。

这一切与“一切皆文件”有什么关系呢?简单的:管道将命令行程序 I/O 放入某种“文件”中。管道是单向的溪流, 不是随机访问就像常规磁盘文件一样,但在许多情况下,差异并不重要。重要的是你可以附加两个独立开发的程序并让它们通过简单的文本进行通信。从这个意义上说,任何两个用Unix方式心里可以沟通。

在您确实需要文件的情况下,可以轻松地将程序输出转换为文件:

$ some-program --some --args > myfile
$ vi myfile

但是,当“一切都是文件”哲学为您提供了更好的方法时,为什么要将输出写入临时文件呢?如果您只想将该命令的输出读入vi编辑器缓冲区,则可以直接从vi“正常”模式执行此操作:

:r !some-program --some --args

将该程序的输出插入到当前光标位置的活动编辑器缓冲区中。在幕后,vi正在使用管道将程序的输出连接到一些代码,这些代码使用与从文件读取相同的操作系统调用。如果 的两种情况:r(即有和没有!)都在vi.我想不出有什么好的理由不这样做。

这也不是 的最新功能vi;很清楚回到古老的 ed(1)文本编辑器。

这个强大的想法在 Unix 中一次又一次地出现。

对于第二个示例,请回忆一下mutt上面的电子邮件命令。我必须将其写为两个单独的命令的唯一原因是我希望命名临时文件,*.gz以便正确命名电子邮件附件。如果我不关心文件名,我可以使用流程替代避免创建临时文件:

$ echo "Here's the disk image I promised to send you." | 
  mutt -a <(gzip -c myfs) -s "Password file disk image" \
      [email protected]

这会将 的输出转换gzip -c为 FIFO(类似于文件)或/dev/fd对象(类似于文件)。⁷

对于这个强大的想法在 Unix 中出现的第三种方式,请考虑gdb在 Linux 系统上。这是用于任何用 C 和 C++ 编写的软件的调试器。从其他系统转向 Unix 的程序员gdb几乎总是会抱怨,“呸,它太原始了!”然后他们去寻找 GUI 调试器,找到现有的几个调试器之一,然后愉快地继续他们的工作……通常从未意识到 GUI 只是gdb在下面运行,在其之上提供了一个漂亮的 shell。大多数 Unix 系统上不存在竞争的低级调试器,因为程序不需要在该级别上竞争。我们所需要的只是一个好的低级工具,如果该低级工具可以通过管道轻松通信,那么我们都可以将高级工具建立在该低级工具的基础上。

这意味着我们现在有了一个记录在案的调试器接口,可以直接替换gdb.不幸的是,主要竞争对手gdb 没有走这条低摩擦的道路,但撇开这个争论不谈,lldb它和 一样可编写脚本gdb

为了在 Windows 机器上实现同样的功能,可替换工具的创建者必须定义某种正式的插件或自动化 API。这意味着除了最流行的程序之外,这种情况不会发生,因为构建普通的命令行用户界面和完整的编程 API 需要大量工作。

这种魔力是通过普遍存在的基于文本的恩典而发生的工控机

虽然Windows的内核具有 Unix 风格的匿名管道,很少看到普通用户程序使用它们工控机在命令 shell 之外,因为 Windows 缺乏首先在命令行版本中创建所有核心服务,然后在其上单独构建 GUI 的传统。这导致没有 GUI 就无法做一些事情,这也是为什么有这么多的原因之一远程桌面系统对于 Windows,与 Linux 相比。这无疑是 Linux 成为云操作系统的部分原因,其中一切都是通过远程管理完成的。命令行界面比 GUI 更容易实现自动化,这在很大程度上是因为“一切都是文件”。

考虑 SSH。你可能会问,它是如何工作的? SSH 将网络套接字(类似于文件)连接到伪终端at /dev/pty*(类似于文件)。现在,您的远程系统已通过一种与 Unix 方式无缝匹配的连接连接到本地系统,您可以通过 SSH 连接传输数据, 如果你需要。

您现在知道这个概念有多强大了吗?

从程序的角度来看,管道文本流与文件没有什么区别,只是它是单向的。程序从管道中读取数据的方式与从文件中读取数据的方式相同:通过文件描述符。 FD 绝对是 Unix 的核心;文件、管道和网络套接字都使用相同的 I/O 抽象这一事实应该告诉您一些信息。

Windows 世界缺乏这种简单文本通信的传统,只能用重量级通信来凑合面向对象编程接口通过串行通信或者。网。如果您需要自动化此类程序,则还必须编写 COM 或 .NET 程序。这比在 Unix 机器上设置管道要困难一些。

缺乏这些复杂编程 API 的 Windows 程序只能通过简陋的接口进行通信,例如剪贴板或文件/保存,然后是文件/打开。

长答案,第 3 部分:注册表与配置文件

Windows 注册表和 Unix 系统配置方式之间的实际差异也说明了“一切都是文件”理念的好处。

在 Unix 类型的系统上,我只需通过检查文件就可以从命令行查看系统配置信息。我可以通过修改这些相同的文件来更改系统行为。大多数情况下,这些配置文件只是纯文本文件,这意味着我可以使用 Unix 上任何可以处理纯文本文件的工具来操作它们。

脚本编写Windows 上的注册表并不那么容易。

最简单的方法是通过一台计算机上的注册表编辑器 GUI 进行更改,然后regedit通过*.reg文件盲目地将这些更改应用到其他机器。这并不是真正的“脚本”,因为它不允许您有条件地执行任何操作:要么全有,要么全无。

如果您的注册表更改需要任何数量的逻辑,下一个最简单的选择是学习电源外壳,相当于学习.NET系统编程。这就好像 Unix 只有 Perl,而你必须做所有的事情特别指定通过它进行系统管理。现在,我是 Perl 粉丝,但不是每个人都是。 Unix 允许您使用任何您喜欢的工具,只要它可以操作纯文本文件。


脚注:

  1. 计划9修复了这个设计失误,通过暴露网络 I/O虚拟/net文件系统

    bash 有/dev/tcp允许通过常规文件系统功能进行网络 I/O。由于它是 Bash 功能,而不是内核功能,因此在 Bash 外部或系统上不可见。根本不使用 Bash 的系统。通过反例,这说明了为什么通过文件系统使所有数据资源可见是一个好主意。

  2. 我所说的“现代 Windows”是指 Windows NT 及其所有直接后代,其中包括 Windows 2000、所有版本的 Windows Server 以及从 XP 开始的所有面向桌面的 Windows 版本。我使用该术语来排除基于 MS-DOS 的 Windows 版本,即 Windows 95 及其直接后代、Windows 98 和 Windows ME,以及它们的 16 位前身。

    您可以看到后面这些操作系统中缺乏统一 I/O 系统的区别。ReadFile()在 Windows 95 上您无法传递 TCP/IP 套接字;您只能将套接字传递给 Windows 套接字 API。请参阅安德鲁·舒尔曼 (Andrew Schulman) 的开创性文章,Windows 95:它不是什么更深入地探讨这个主题。

  3. 毫无疑问,/dev/null它是 Unix 类型系统上真正的内核设备,而不仅仅是表面上等价的特殊大小写的文件名NUL在Windows中。

    尽管 Windows 尝试阻止您创建NUL文件,但还是有可能绕过此保护仅仅用诡计,欺骗Windows的文件名解析逻辑。如果您尝试使用 或 Explorer 访问该文件cmd.exe,Windows 将拒绝打开它,但您可以通过 Cygwin 写入它,因为它使用与示例程序类似的方法打开文件,并且您可以删除它通过类似的诡计

    相比之下,rm /dev/null只要您具有对 的写访问权限/dev,Unix 就会很高兴地让您在其位置重新创建一个新文件,这一切都没有欺骗,因为开发节点只是另一个文件。虽然该 dev 节点丢失,但内核的空设备仍然存在;在您通过以下方式重新创建开发节点之前,它是无法访问的mknod

    您甚至可以在其他地方创建额外的空设备开发节点:无论您是否调用它/home/grandma/Recycle Bin,只要它是空设备的开发节点,它的工作方式就与 完全相同/dev/null

  4. 其实有Windows 中的高级“格式化磁盘”API:SHFormatDrive()Win32_Volume.Format()

    有两个非常……嗯……视窗某种原因。第一个要求 Windows 资源管理器显示其正常的“格式化磁盘”对话框,这意味着它可以在任何现代版本的 Windows 上运行,但仅当用户交互式登录时才有效。另一个可以在后台调用,无需用户输入,但直到 Windows Server 2003 才将其添加到 Windows 中。没错,直到 2003 年,在 Unix 发布的世界中,核心操作系统行为才隐藏在 GUI 后面mkfs 从第一天开始

    /etc/mkfs我的Unix V5 的副本从 1974 年开始是一个 4136 字节静态链接等离子11可执行的。 (Unix没有获得动态链接直到20世纪80年代末,所以它不像其他地方有一个大库在做所有实际工作。)它的源代码 - 包含在 V5 系统映像中,如下所示/usr/source/s2/mkfs.c— 是一个完全独立的 457 行 C 程序。连任何声明都没有#include

    这意味着您不仅可以检查mkfs高级别的功能,还可以使用创建 Unix 时使用的相同工具集进行实验,就像您在肯·汤普森,四十年前。在 Windows 上尝试一下。您今天最接近的方式就是下载MS-DOS源代码,首次发布于2014年,你会发现它只是一堆集会来源。它只会使用您手头可能没有的过时工具进行构建,最终您将获得自己的 MS-DOS 2.0 副本,这是一个远不如 1974 年的操作系统强大的操作系统Unix V5,尽管它是在近十年后发布的。

    (为什么要谈论 Unix V5?因为它是现存最早的完整 Unix 系统。早期版本是显然输给了时间。有一个专案它拼凑了一个 V1/V2 时代的 Unix,但它似乎丢失了mkfs,尽管上面链接的 V1 手册页的存在证明它一定存在于某个地方、某个时间。要么那些将这个项目放在一起的人无法找到mkfs要包含的现有副本,要么我很难找到没有 的文件find(1),而该系统中也不存在该文件。:)

    现在,您可能会想,“我不能直接调用吗format.com?在 Windows 上调用与在 Unix 上调用不是一样吗mkfs?”唉,不,这不一样,原因有很多:

    • 首先,format.com它不是为了脚本化而设计的。它会提示您“准备好后按 ENTER”,这意味着您需要向其输入发送 Enter 键,否则它将挂起。

    • 然后,如果您想要的不仅仅是成功/失败状态代码,您必须打开其标准输出进行读取,即Windows 上的情况比实际情况要复杂得多。 (在 Unix 上,该链接文章中的所有内容都可以通过一个简单的popen(3)称呼。)

    • 在经历了所有这些复杂性之后, 的输出format.com比 的输出更难被计算机程序解析mkfs,主要供人类使用。

    • 如果你跟踪它format.com的内容,你会发现它执行了一系列复杂的调用DeviceIoControl()ufat.dll、 等等。它不仅仅是打开一个设备文件并将新的文件系统写入该设备。这就是你得到的设计一家在全球拥有 221000 名员工的公司并且需要保持雇用他们。

      对比一下当你的核心操作系统工具是由志愿者在业余时间编写时所发生的情况:他们针对自己的问题提出了权宜之计、最少的解决方案,为我们其他人带来了简单性红利。

  5. 当谈论循环设备时,我只谈论 Linux 而不是一般的 Unix,因为循环设备在 Unix 类型系统之间不可移植。 macOS、BSD等都有类似的机制,但语法不同略有不同

  6. 在磁盘驱动器大小如洗衣机、成本比部门主管的豪华汽车还高的时代,与现代计算环境相比,大型计算机实验室将共享更大比例的集体磁盘空间。将远程磁盘透明地移植到本地文件系统的能力使得这种分布式系统更易于使用。/usr/share例如,这就是我们得到的地方。

    对比Windows,其中驱动器号为您提供了很少的符号表达选择;是P:指BigServer上的“公共”空间还是软件镜像服务器上的“packages”目录? UNC 替代方案要求您记住远程文件所在的服务器,这在拥有数百或数千个文件服务器的大型组织中变得很困难。

    Windows 直到 2007 年 Vista 推出后才获得符号链接NTFS 符号链接,而且它们不是制造出来的可用的 直到十年后。 Windows 的符号链接比 Unix 的符号链接更强大——这是 Unix 的一个特性自1977年以来- 因为它们还可以指向远程文件共享,而不仅仅是本地路径。 Unix 的做法不同,通过1984年的NFS,它建立在 Unix 现有的之上挂载点功能,这是它从一开始就拥有的功能。

    因此,Windows 落后 Unix 大约 2、3 或 4 个十年,这取决于你如何看待它。你可能会反对,“但它有 Unix 风格的符号链接现在!“然而,这没有抓住要点,因为这意味着不存在有数十年历史的传统使用它们在 Windows 上运行,因此在 Unix 系统普遍使用它们的世界中人们意识不到它们。如果不了解符号链接,就不可能长时间使用 Unix 系统。

    Windows 没有帮助MKLINK程序向后,并且您仍然无法从 Windows 资源管理器创建它们,而 Unix 相当于 Windows 资源管理器通常让您创建符号链接。

  7. Bash 根据系统的功能选择方法,因为/dev/fd并非在所有地方都可用。

答案2

好吧,作为一种简化本身,“一切都是文件”必须需要一个简单的表征。

当 Ken Thompson 和 Dennis Ritchie 于​​ 1969 年开始构建 UNIX 系统时,他们发现了一种可以简化计算机与人之间交互的许多方面的结构。 Thompson 和 Ritchie 的目标是保持他们的系统简单,他们发现了一组原语,使他们能够用很少的原语做很多事情。

--AT&T 档案:UNIX 操作系统 4:35

“一切都是文件”这个想法反映了这样一个事实:任何用户操作都会导致在操作系统控制的地址处读取或写入位。这些是系统调用、分配的内存和硬件设备的地址。通过 UNIX Shell 公开这些地址,用户无需额外的软件即可执行各种任务。

您可以通过读取、写入或执行文件来与 UNIX Shell 进行交互,其中“文件”字面意思是您可以写入、读取或执行的内容。这允许脚本和用户以完全相同的方式与机器交互,从而简化了交互的某些方面。

Windows 没有这样的理念。尽管它在许多情况下提供了等效的功能,但它缺乏原则。例如,Windows Shell 是可视化的。对于最终用户来说,每组任务往往都有自己的工具集,而这些工具集的绑定通常需要其他工具集。

答案3

如果您Linux认为英语file systems分别是字母表这基本上是基础块英语

维基百科文件系统页面,

在计算中,文件系统(或文件系统)用于控制数据的存储和检索方式。如果没有文件系统,放置在存储区域中的信息将是一大堆数据,无法区分一条信息在哪里停止以及下一条信息在哪里开始。

因此,用外行人的话来说,如果没有英语的正确语言结构(由字母组成),人类互动就不会产生任何意义。同样,如果没有文件系统,底层存储设备中的任何数据都将没有任何实际意义。

相关内容