Skip to content

Latest commit

 

History

History
230 lines (106 loc) · 20.2 KB

6.文件管理.md

File metadata and controls

230 lines (106 loc) · 20.2 KB

6.文件管理

操作系统为磁盘提供的抽象就是:文件及文件系统,或者说,文件系统就是磁盘的抽象。它是一个子虚乌有的东西,看不见、摸不着,但却可以使用的介于磁盘与用户之间的界面。

文件管理系统是一组系统软件,它为使用文件的用户和应用程序提供服务,包括文件访问、目录维护和访问控制。文件管理系统通常被视为一个由操作系统提供服务的系统服务,而不是操作系统的一部分,但是在任何系统中,至少有一部分文件管理功能是由操作系统执行的。

简单地说,文件系统将其接触的磁盘物理特性转换为用户看到的路径名和文件名。用户对磁盘进行访问只需要给出文件名和路径名即可,而无须知道磁柱、磁道、扇面、数据块等信息。

文件系统不但提供存储数据(组织为文件)的手段,而且提供一系列对文件进行操作的功能接口。典型的操作如下:

  • 创建
  • 删除
  • 打开
  • 关闭

与内存管理系统类似,文件系统也需要达到两个目的:地址独立和地址保护。地址独立就是一个文件在产生的时候无须担心其存放的磁盘地址,即文件数据的产生与文件将来存放的磁盘地址相互独立。而地址保护则需要对文件的访问进行一定的限制,即不是任何人都可以访问任何文件的。注意,这里的保护与内存地址保护是有区别的。内存管理下地址保护指的是一个进程不能访问另一个进程空间,而这里的保护不是一个文件不能访问另一个文件空间,而是一个文件的访问是有限制的。

文件系统架构

img

  • 设备驱动(device drivers):程序直接与外围设备通信。设备驱动程序负责启动设备上的I/O操作,处理I/O请求的完成。
  • 基本文件系统或物理I/O层:是与计算机系统外部环境的基本接口。这一层处理在磁盘间或磁带系统间交换的数据块,因此它关注的是这些块在辅存和内存缓冲区中的位置,而非数据的内容或所涉及的文件结构。
  • 基本I/O管理程序(basic I/O supervisor);负责所有文件I/O的初始化和终止。在这一层,需要一定的控制结构来维护设备的输入/输出、调度和文件状态。基本I/O管理程序是操作系统的一部分。
  • 逻辑I/O(logical I/O)使用户和应用程序能够访问记录。因此基本文件系统处理的是数据块,而逻辑I/O模块处理的是文件记录。逻辑I/O提供一种通用的记录I/O的能。

文件

文件是将数据存储在磁盘等存储媒介中的一种形式。文件是字节数据的集合体。程序文件中存储数据的单位是字节。文件的大小之所以用××KB、××MB等来表示,就是因为文件是以字节(B=Byte)为单位来存储的。用1字节(=8位)表示的字节数据有256种,用二进制数来表示的话,其范围就是00000000~11111111。如果文件中存储的数据是文字,那么该文件就是文本文件。如果是图形,那么该文件就是图像文件。在任何情况下,文件中的字节数据都是连续存储的。

Image

地址独立的实现机制:文件夹

给一个文件名,操作系统是怎么知道从什么地方读取文件的内容呢?或者说,文件在磁盘上的什么位置,操作系统是如何知道的呢?这当然需要一个数据结构来记录每个文件在磁盘上的地址,这个数据结构就是文件夹。

文件夹也称为目录夹(folder),它保存的不是用户数据,而是关于文件及文件系统的信息。简单地说,文件夹的角色就是跟踪文件,里面存放的是从文件到文件在磁盘的地址的映射,即“文件名→文件在磁盘上的地址”。文件夹对于文件来说,就相当于动态地址翻译对于虚拟地址的作用,即从虚拟地址到实际地址的一种翻译机制。

这样,当给予一个文件名时,我们从文件夹里可以找到该文件所在的位置,从而发出正确的读取命令。那么文件在磁盘上的地址是什么呢?这就要看文件在磁盘上是如何存放了。如果文件在磁盘上是连续存放的,即一个文件占用一片连续的磁盘空间,则文件在磁盘上的地址就是该文件的第一块数据块所在的磁盘扇面号。当然,文件的存放不只有连续存放一种模式,因此文件在磁盘上的地址还有别的形式,我们稍后解释。但总的一点就是,文件夹应该提供一种找出一个文件所有磁盘块的方法。

既然文件夹存放的是文件名到磁盘地址的映射,那么自然地我们可以以一个简单数组来实现。这个数组是以文本文件存放的,你可以自己写一个程序打开这个文件就可以看到里面的内容。而读取文件夹的内容就变成读取这个简单数组的内容。而这正是shell utility"ls"(UNIX、Linux、Windows Power Shell)和"dir"(Windows)所做的事情。这里很重要的一点是文件夹本身也是文件。因此,我们可以像对待文件一样对待文件夹:使用同样的存储结构来存放文件夹的数据;文件夹里面的每个记录本身又可以是文件夹。

由于文件夹里面又可以有文件夹,这样就形成了一个层次结构。这个层次结构的顶端就是根文件夹,也称为根目录,根目录是一个文件系统的总起点,它在操作系统启动的时候加载到内存。从根目录开始,该文件系统里面的所有文件都可以找出来。由于根目录是整个文件系统的源点,如果根目录损坏,则整个文件系统都无法访问,也就说文件系统已经崩溃。

假定我们要找的文件是:/zou/cs307/file.pdf。这个文件名最前面的“/”代表的就是根目录。因此,我们首先从根目录开始寻找。根目录里面可能保护许多文件和文件夹,其中的一个文件夹就是zou,而根目录里面存放有zou文件夹所在的磁盘地址。我们获得的磁盘地址读取zou的内容(一个文本数组),将其打开。我们发现,打开的zou文件夹里又包括另一个文件夹cs307及其在磁盘上的位置。我们从给出的磁盘位置上读取文件夹cs307的内容(另一个文本数组),发现里面包括一个文件名为file.pdf的文件及其对应的磁盘地址。我们从给出的磁盘地址上就可以读取文件file.pdf的内容。这样从根目录开始,我们需要访问磁盘3次才能得到文件file.pdf的内容。

相对路径和绝对路径

文件名"zou/cs307/file.pdf"包括从根目录开始的一个完全路径,因此称为绝对路径。绝对路径是从根目录的视角看一个文件的访问路径。与此相对的是相对路径,即从某个工作目录,如用户当前所处的目录的角度看某一个文件的访问路径。例如,如果我们目前已经在目录"zou/cs307/"下,则只需要给出文件名file.pdf即可访问该文件。使用相对路径的好处是没有必要寻遍整个目录夹,可以节省磁盘访问次数,从而提高文件访问效率。前面讲过,从根目录开始需要3次磁盘访问才能得到文件file.pdf的内容;但如果在工作目录"zou/cs307/"下直接给出文件名file.pdf,则只需要进行一次磁盘访问即可。因为当前目录的内容是已经打开的,即已经加载到内存的,我们无须磁盘访问即可查看当前目录内容,发现file.pdf的文件及其对应的磁盘地址。我们只需要按照该地址对磁盘进行一个访问即可获得file.pdf的内容。

但需要注意的是,相对路径不一定总是比绝对路径节省磁盘访问次数。如果你要访问的文件是当前这个目录的“直系后代”,则将节省磁盘访问次数;否则,即如果不是“直系后代”,你就可能多花费时间。

文件拷贝程序的工作原理很简单:打开源文件和目标文件,循环往复地将源文件的内容读到一个缓冲区,然后将缓冲区的内容写到输出目标文件里。当拷贝结束后,关闭源文件和目标文件。上述程序中使用的文件系统调用包括fin.open、fout.open、fin.get等。这里需要注意的是,输入文件的打开模式为只读,而输出文件的打开模式是只有创立这个文件的人才能读写,其他人均无访问权限(输出模式0700代表的就是这个意思)。那么上述程序有一个问题,不知读者是否看出?在正确性方面没有问题,但在效率上却不太令人满意。这是因为该程序的系统调用次数太多,而系统调用需要陷入到内核。另外,每次系统调用都需要进行磁盘操作,而磁盘操作的效率远远低于内存操作的效率。解决这个问题的办法是可以将缓冲区变大,从而降低系统调用和磁盘访问的次数。但缓冲区越大,需要的内存就越大,将造成程序的可执行性下降。而且,就算缓冲区很大,文件还可以更大,还是不能从根本上解决效率低的问题。那有没有其他办法解决效率低下的问题呢?当然有,就是不使用系统调用。而是采用我们下面将要讲述的内存映射的文件访问。

内存映射的文件访问

解决读写文件效率低的中心思想就是把磁盘访问变成内存访问。实现这种访问转变的手段就是内存映射的文件访问。内存映射的文件访问原理很简单:把需要访问的文件映射到一个进程的虚拟地址内。这样,访问该虚拟地址就相当于访问文件。这样文件访问就变成内存访问。例如,假定我们将一个32KB大小的test文件映射一个进程的虚地址1024KB处。那么读写1024KB的地址就是读写test这个文件的第一个字节。而当该进程中止的时候,与文件对应的这个内存内容将被冲洗(flush)到磁盘上。

这样做的好处就是磁盘访问变成了内存访问,效率自然提高。例如,还是我们前面的文件拷贝,这次不用文件系统调用来实现,而是使用内存映射的文件访问来实现。例如,我们可以将文件a和b均映射到同一个进程的不同虚拟地址上。这样读a文件对应的虚地址将把a的内容从磁盘上拷贝到内存,再把内容写到b对应的虚地址上即可。不过需要提醒的是,内存映射的文件访问不是为了文件拷贝而提出的,而是为了文件的共享。具体来说,我们可以将同一个文件映射到两个或多个进程的虚地址空间。这样每个进程对相应的虚地址空间进行访问时就是对同一个文件进行访问。

这里我突然不明白内存映射和DMA有什么区别?

  1. https://stackoverflow.com/questions/3851677/what-is-the-difference-between-dma-and-memory-mapped-io

    Memory-mapped I/O allows the CPU to control hardware by reading and writing specific memory addresses. Usually, this would be used for low-bandwidth operations such as changing control bits.

    DMA allows hardware to directly read and write memory without involving the CPU. Usually, this would be used for high-bandwidth operations such as disk I/O or camera video input.

  2. https://www.quora.com/What-is-the-difference-between-direct-memory-access-and-memory-mapped-I-O-systems

    Direct memory access and memory-mapped I/O are related as you’ll see below, but they are not opposites and can’t be compared directly.

    Memory-mapped I/O and port-based I/O

    There are two principal types of input/output in microprocessors and microcontrollers (and other types of computers too) — memory-mapped I/O and port-based I/O.

    Memory-mapped I/O means a peripheral device is addressed just the same as memory, and resides in the same address space as data memory.

    With port-based I/O, ports are addressed using special instructions such as in for input and out for output, followed by the port number, usually 0–255 or 0–65535. The port number will usually be output on the same address bus as would be used for a memory reference, but there will be a pin such as [math]M/\overline{IO}[/math] on the microprocessor which is brought high to indicate an peripheral device is being addressed instead of memory.

    Port addressing was popular in early microprocessors, because it didn’t take away addresses from the limited (typically 64K) address map of an 8 or 16-bit processor. Note that any microprocessor that supports port addressing can also use memory-mapped I/O as well.

    Direct memory access

    Direct memory access (DMA) allows some peripherals to access RAM memory directly, without the CPU being involved. DMA can also be used to move data from one part of RAM to another.

    Without DMA, an application like streaming I/O would be extremely processor intensive, even with a deep FIFO in the peripheral, since the processor would have to write each datum to the the peripheral.

    Say it is streaming data from a hard drive. With DMA, it can set up a pointer to the beginning of a block of data in memory equal to a sector on the disk, initiate the transfer with the disk controller, and go away until it gets an interrupt that the transfer has completed. The processor may then have to decode the data if it is in a format like MP3. Then it can send the decoded out to a AC97 or I2S codec so the user hears the sound in their headphones, again without the processor actually having to move any data itself.

    Without DMA, our machines would feel extremely sluggish or not be usable at all.

Android文件系统

Android使用了Linux中的文件管理功能。Android文件系统目录与Linux安装目录类似,只是前者有一些特有的特性。

img

Android文件系统目录的顶层部分:

  • system目录包含操作系统的核心部分,核心部分包括系统的二进制文件、系统库文件和配置文件。它还包含Android的基本应用,如闹钟、计算器和相机。系统映像是锁定的,文件系统只为用户提供只读权限。
  • data目录是应用程序存储文件时的首选位置。当系统中安装了一个新的应用程序时,以下这些操作都有data目录有关:
    • .apk文件放置在/data/app中
    • 以应用为中心的库文件安装在/data/data/<应用名称>目录中。这个目录是特定应用程序的沙盒区域,只有该应用可以访问,其他应用不能访问。
    • 建立应用相关的文件数据库。
  • cache目录用于存储应用的临时数据。该区域存储Android系统频繁访问的数据和应用组件。清理高速缓存不会影响到个人数据,而只会简单地清理其中的已有数据,继续使用设备时,其中的数据会自动创建。
  • mnt/sdcard目录不是设备内部的内存分区,而是sd卡的分区,sd卡是一种用于android设备的非易失性的存储卡。

文件系统布局

文件系统存储在磁盘中。大部分的磁盘能够划分出一到多个分区,叫做磁盘分区(disk partitioning) 或者是磁盘分片(disk slicing)。每个分区都有独立的文件系统,每块分区的文件系统可以不同。磁盘的 0 号分区称为 主引导记录(Master Boot Record, MBR),用来引导(boot) 计算机。在 MBR 的结尾是分区表(partition table)。每个分区表给出每个分区由开始到结束的地址。

当计算机开始引 boot 时,BIOS 读入并执行 MBR。

一个磁盘包括一个个扇面,编号从0递增,整数计数。第0个扇面在整个文件系统中占有重要意义。该扇面存放的是主引导记录(Master Boot Record,MBR)。该记录的内容是一个小小的程序,用来启动计算机。如果该扇面损坏,则整个磁盘就无法使用。这时唯一的办法就是将磁盘拿到硬件制造商那里对磁盘扇面重新编号。在MBR后面紧接着的是磁盘分区表。磁盘分区表里给出的是磁盘的所有分区及其开始和终结地址,其中一个分区为主分区。操作系统就装载在这个分区中。主分区里面最前面的是引导记录(Boot Record)。在计算机启动时,处于主板ROM里面的BIOS程序首先运行。BIOS在进行一些基本的系统配置扫描后对磁盘的扇面0进行读操作,将MBR里面的程序读到内存并运行。MBR程序接下来找到系统主分区,并将主分区里面的Boot Record加载并运行。Boot Record里面的内容是一个小程序,该程序将负责找到操作系统映像,并加载到内存,从而启动操作系统。所有的文件系统都必须按照这种格式存放,操作系统才能正常启动。

在Boot Record记录块后面的内容就因情况而异了。一般来说,在该记录块后面的磁盘内容布局因文件系统的不同可以不同。但在通常情况下,紧接着引导记录后面的是一个超级数据块(Super Block),该块里面存放的是关于该文件系统的各种参数,如文件系统类型、文件系统数据块尺寸等。在Super Block后面则是磁盘的自由空间,其后面是I-NODE区,再后面是文件系统根目录区。分区的最后存放的是用户文件和文件夹区。

引导块

MBR 做的第一件事就是确定活动分区,读入它的第一个块,称为引导块(boot block) 并执行。引导块中的程序将加载分区中的操作系统。为了一致性,每个分区都会从引导块开始,即使引导块不包含操作系统。引导块占据文件系统的前 4096 个字节,从磁盘上的字节偏移量 0 开始。引导块可用于启动操作系统。

除了从引导块开始之外,磁盘分区的布局是随着文件系统的不同而变化的。通常文件系统会包含一些属性,如下

image

超级块

紧跟在引导块后面的是 超级块(Superblock),超级块 的大小为 4096 字节,从磁盘上的字节偏移 4096 开始。超级块包含文件系统的所有关键参数

  • 文件系统的大小
  • 文件系统中的数据块数
  • 指示文件系统状态的标志
  • 分配组大小

在计算机启动或者文件系统首次使用时,超级块会被读入内存。

空闲空间块

接着是文件系统中空闲块的信息,例如,可以用位图或者指针列表的形式给出。

碎片

这里不得不提一个叫做碎片(fragment)的概念,也称为片段。一般零散的单个数据通常称为片段。磁盘块可以进一步分为固定大小的分配单元,片段只是在驱动器上彼此不相邻的文件片段。

inode

然后在后面是一个 inode(index node),也称作索引节点。它是一个数组的结构,每个文件有一个 inode,inode 非常重要,它说明了文件的方方面面。每个索引节点都存储对象数据的属性和磁盘块位置。

inode 节点主要包括了以下信息

  • 模式/权限(保护)
  • 所有者 ID
  • 组 ID
  • 文件大小
  • 文件的硬链接数
  • 上次访问时间
  • 最后修改时间
  • inode 上次修改时间

文件分为两部分,索引节点和块。一旦创建后,每种类型的块数是固定的。你不能增加分区上 inode 的数量,也不能增加磁盘块的数量。

紧跟在 inode 后面的是根目录,它存放的是文件系统目录树的根部。最后,磁盘的其他部分存放了其他所有的目录和文件。

文件的实现

最重要的问题是记录各个文件分别用到了哪些磁盘块。不同的系统采用了不同的方法。下面我们会探讨一下这些方式。分配背后的主要思想是有效利用文件空间快速访问文件 ,主要有三种分配方案

  • 连续分配
  • 链表分配
  • 索引分配