3

我有一个小问题:

例如,我正在使用 .NET Framework 中的 System.IO.File.Copy() 方法。此方法是 WinAPI 中 CopyFile() 函数的托管包装器。但是 CopyFile 函数是如何工作的呢?它与硬盘的固件交互,或者可能通过汇编程序执行其他一些操作,或者可能是其他一些东西......

从最高层到最低层是怎样的?

4

3 回答 3

10

最好从底部开始,然后逐步向上。

磁盘驱动器在最低级别被组织成扇区、磁道和磁头的集合。扇区是磁道的段,磁道是磁盘本身的区域,由磁盘在其下方旋转时的磁头位置表示,磁头是从磁盘读取数据的实际元素。

由于磁道是根据磁头到磁盘中心的距离来测量的,因此您可以看到磁道的“长度”在磁盘的中心处是如何短于磁盘外边缘的“长度”。

扇区是轨道的片段,通常具有固定长度。因此,内部磁道将比外部磁道容纳更少的扇区。

如今,大部分磁盘几何结构由驱动器控制器自己处理,尽管过去该组织由操作系统和磁盘驱动程序直接管理。

驱动电子设备和磁盘驱动程序合作尝试将磁盘表示为一系列固定长度的块。

因此,您可以看到,如果您有一个 10MB 的驱动器,并且您使用 512 字节的磁盘块,那么该驱动器将有 20,480 个“块”的容量。

这个块组织是建立其他一切的基础。一旦你有了这个能力,你就可以通过磁盘驱动程序和驱动控制器告诉磁盘去磁盘上的一个特定块,并用新数据读/写那个块。

文件系统将这堆块组织到它自己的结构中。FS 必须跟踪正在使用哪些块,以及哪些文件正在使用。

大多数文件系统都有一个固定的“启动位置”,即启动时可以去尝试查找有关磁盘布局的信息的某个位置。

考虑一个没有目录的原始文件系统,并且支持具有 8 个字母名称和 3 个字母扩展名的文件,加上 1 个字节的状态信息,以及文件在磁盘上开始的块号的 2 个字节。我们还可以假设系统有 1024 个文件的硬限制。最后,它必须知道磁盘上的哪些块正在被使用。为此,它将使用每块 1 位。

此信息通常称为“文件系统元数据”。当磁盘被“格式化”时,现在只需编写新的文件系统元数据即可。在过去,实际上是在空白磁介质上写入扇区标记和其他信息(通常称为“低级格式”)。今天,大多数驱动器已经具有低级格式。

对于我们粗略的示例,我们必须为目录分配空间,并为“目录”分配空间,该数据表明正在使用哪些块。

我们还会说文件系统必须从第 16 块开始,以便操作系统可以使用前 16 个块作为“引导扇区”。

因此,在第 16 块,我们需要存储 14 个字节(每个文件条目)* 1024(文件数)= 12K。除以 512(块大小)为 24 个块。对于我们的 10MB 驱动器,它有 20,480 个块。20,480 / 8(8 位/字节)是 2,560 字节 / 512 = 5 个块。

在磁盘上可用的 20,480 个块中,文件系统元数据是 29 个块。为操作系统添加 16 个,即 20,480 个块中的 45 个,留下 20,435 个“空闲块”。

最后,每个数据块保留最后 2 个字节以指向文件中的下一个块。

现在,要读取文件,您可以在目录块中查找文件名。从那里,您可以找到文件第一个数据块的偏移量。您读取该数据块,获取最后两个字节。如果这两个字节是 00 00,那么这就是文件的结尾。否则,取该数字,加载该数据块,然后继续读取整个文件。

文件系统代码在末尾隐藏了指针的细节,只是将块加载到内存中,供程序使用。如果程序执行 read(buffer, 10000),您可以看到这将如何转换为从磁盘读取几个数据块,直到缓冲区被填满或到达文件末尾。

要写入文件,系统必须首先在目录中找到空闲空间。一旦有了它,它就会在 TOC 位图中找到一个空闲块。最后,它获取数据,写入目录条目,将其第一个块设置为位图中的可用块,切换位图上的位,然后获取数据并将其写入正确的块。系统将缓冲此信息,以便理想情况下只需在块已满时写入一次。

在写入块时,它会继续消耗 TOC 中的位,并在执行过程中将块链接在一起。

除此之外,“文件复制”是一个简单的过程,从系统利用文件系统代码和磁盘驱动程序。文件副本只是简单地读入一个缓冲区,将其填满,然后将缓冲区写出。

文件系统必须维护所有元数据,跟踪您从文件读取的位置或写入的位置。例如,如果您只从文件中读取 100 字节,显然系统将需要读取整个 512 字节数据块,然后当您尝试从文件中读取另外 100 字节时“知道”它位于字节 101 上。

另外,我希望很明显,这是一个非常非常粗糙的文件系统布局,有很多问题。

但是基本原理已经存在,所有文件系统的工作方式都与此类似,但细节差异很大(大多数现代文件系统不再有硬性限制,举个简单的例子)。

于 2009-12-26T16:54:54.077 回答
5

这是一个要求很高的问题或一个很长的答案,但我试图让它简短。

基本上,.NET Framework 包装了一些“本机”调用,即在较低级别库中处理的调用。这些较低级别的调用通常包装在缓冲区逻辑中,以隐藏复杂的东西,例如同步文件内容。

下面是与操作系统内核交互的本机级别。内核,任何操作系统的核心,然后将您的高级指令转换为您的硬件可以理解的东西。例如,Windows 和 Linux 都使用硬件抽象层,这是一个将硬件特定细节隐藏在通用接口后面的系统。为特定设备编写驱动程序只是实现特定设备必须提供的所有方法的任务。

在你的硬件上调用任何东西之前,文件系统就会参与进来,文件系统本身也会缓冲和缓存很多,但又是透明的,所以你甚至不会注意到这一点。调用队列中的最后一个元素是设备本身,同样,大多数设备都符合某些标准(如 SATA 或 IDE),因此可以以类似的方式进行接口。

我希望这有帮助 :-)

于 2009-12-25T19:45:59.617 回答
2

.NET 框架调用 Windows API。

Windows API 具有跨各种文件系统管理文件的功能。

然后它取决于有问题的文件系统。请记住,它不一定是 HDD 上的“普通”文件系统。它甚至可以是一个 shell 扩展,它只是模拟一个驱动器并将数据保存在你的 gmail 帐户或其他任何东西中。关键是 Windows API 中的相同文件操作函数被用作对许多可能的较低数据层的抽象。

所以答案真的取决于你感兴趣的文件系统类型。

于 2009-12-25T19:40:22.470 回答