文件

重点内容,需熟练掌握文件的物理结构,也需了解文件的元信息和逻辑结构,常在选择题中考察,也会在大题中考察。

文件元信息

在 UNIX 和类 UNIX 操作系统中,inode(索引节点)用于存储文件的元数据,它包含了关于该文件的大部分元数据,但不包括文件名和文件实际内容。

inode 中包含如下信息:

  • 文件类型:文件是普通文件、目录还是链接文件等。
  • 权限:文件的访问控制信息,如用户、组和其他用户的读、写、执行权限。
  • 所有者:文件的所有者和组 ID。
  • 大小:文件的大小(字节数)。
  • 时间戳:文件的创建时间、最后修改时间和最后访问时间。
  • 链接计数:指向该文件的硬链接数量。当计数为 0 时,文件会被删除。
  • 数据块指针:文件内容所在的数据块(block)的位置信息。这包括直接指针、间接指针、二级间接指针和三级间接指针,用于指向存储文件内容的磁盘块。

进程文件管理

在进程中可能会打开若干文件,操作系统需要以某种方式对进程打开的文件信息进行记录。这就需要用到三个数据结构:文件描述符表、文件表、inode 表。

  • 文件描述符表(File Descriptor Table):每个进程都有自己的文件描述符表,这个表对应于该进程打开的文件描述符。文件描述符是进程范围内的一个小的非负整数。
  • 文件表(File Table):操作系统维护一个全局的文件表,该表记录了所有打开文件的状态信息。每次 open 调用成功时,都会创建一个新的文件表条目。
  • INode 表(INode Table):系统还维护了一个 inode 表,该表包含了文件的元数据和指向文件实际数据的指针。如果多个进程打开同一个文件,它们的文件表条目将指向 inode 表中的同一 inode。

在进程 1 中执行fdA1 = open("fileA.txt", O_RDONLY)fdAdup = dup(fdA)系统调用,在进程 2 中执行fdA2 = open("fileA.txt", O_RDONLY),系统中的数据结构如下图所示:

file descriptor
file pointer
fdA1
1
file descriptor
file pointer
fdA2
3
fdAdup
1
file offset
access mode
reference count
inode pointer
0
O_RDONLY
2
4
1
O_RDONLY
1
4
0
1
2
3
4
5
file type
file locks
file properties
Regular
...
...
0
1
2
3
4
5
6
Process A File Descriptor Table
Process B File Descriptor Table
Open File Table
Inode Table

文件的逻辑结构

  1. 顺序文件
    • 记录顺序:顺序文件中的数据以记录的顺序排列。每个记录都按顺序存储在文件中,一个接一个。
    • 访问方式:顺序文件通常按照记录的顺序进行访问。要查找或修改文件中的某个记录,必须从文件的开头开始逐个读取记录,直到找到所需的记录或到达文件末尾。
    • 应用场景:适用于需要按顺序读取和处理数据的应用程序,如批处理任务和日志文件。
  2. 随机文件
    • 记录随机排列:在随机文件中,数据记录可以按任意顺序排列,而不需要按照特定的顺序。
    • 访问方式:随机文件支持随机访问,这意味着可以直接访问文件中的任何记录,而不必按照顺序逐个读取。
    • 存储结构:为了支持随机访问,随机文件通常使用链接结构或索引结构来跟踪记录的位置。
    • 应用场景:适用于需要快速查找和访问特定记录的应用程序,如数据库系统和文件系统。

文件的物理结构

文件的物理结构,也称为文件的存储结构,是指文件在物理存储介质(如硬盘、固态硬盘等)上的实际存储方式。

与文件的逻辑结构(用户看到的组织形式)不同,物理结构是操作系统层面如何管理文件数据的。

文件的物理结构包含以下几个种类:

连续分配方案

连续分配该方案中,每个文件占用磁盘上一个连续的块集。例如,如果一个文件需要 n 个块,并且给定一个块 b 作为起始位置,那么分配给该文件的块将是 b, b+1, b+2,……b+n-1。这意味着给定起始块地址和文件的长度,我们可以确定文件占用的块。具有连续分配的文件的目录条目包含分配部分的起始块长度的地址。

连续分配方案对应的目录包含如下信息:

  • 文件的其实地址
  • 分配块数量

下图中的文件 “file3” 从区块 19 开始,长度为 6 个区块。因此,它占用 19、20、21、22、23、24 个块。

0
1
2
3
4
5
6
7
8
9
10
3
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
file
start
length
file1
0
2
file2
14
3
file3
19
6
file4
28
4
file5
6
2
Directory
  • 优点:因为由于文件块的连续分配,查找的次数很少,访问速度非常快。
  • 缺点:存在内部和外部碎片化的问题,使得内存利用效率低下。增加文件大小困难,因为它取决于特定实例中连续内存的可用性。

链式分配

在这个方案中,每个文件都是一个不需要连续的磁盘块链表。磁盘块可以分散在磁盘上的任何地方。目录项包含指向起始和结束文件块的指针。每个块包含一个指针,指向文件所占用的下一个块。

目录项包含指向起始和结束文件块的指针。每个块包含一个指针,指向文件所占用的下一个块。

下图中的文件 “file1” 显示了块是如何随机分布的。最后一个块(25)包含-1,表示空指针,不指向任何其他块。

0
1:10
2
3
4
5
6
7
8
9:16
10:25
3
12
13
14
15
16:1
17
18
19
20
21
22
23
24
25:-1
26
27
28
29
30
31
file
start
end
file1
9
25
Directory
  • 优点:可以充分利用磁盘空间,不会遭遇到碎片的问题。
  • 缺点:因为文件是在磁盘上随机分配的,所以访问一个文件的过程需要经历多次磁盘的检索,这使得文件读取的速度变慢。

文件分配表

基于链表的链接为隐式链接,每个磁盘块的末尾包含文件的下一个盘块号。

文件分配表 FAT 也是基于链接的方式,不过文件的链接方式是存储在一个表格当中,表格包含两列,一列是盘块号,另一列是在该文件盘块之后的下一个盘块号,这种方式也叫做显式链接。

FILENAME
· · · · · ·
START
file1
· · · · · ·
2
file2
· · · · · ·
7
BLOCK NUM
NEXT
0
-2
1
-1
2
8
3
-2
4
-2
5
-1
6
-2
7
1
8
5
9
-2
· · · ·
· · · · 
98
-2
99
-2

索引分配

在索引分配中,一个文件所占用的所有盘块号被存储在另一个盘块中,这个盘块叫做叫做索引块。

假设一个文件的大小是 4MB,一个盘块的大小为 4KB,盘块编号的大小为 4B(32位)。 在这种情况下,文件需要用 1K 个盘块存储,索引块的内容如下图所示:

block 0
numer
block 1
numer
block 2
numer
block 3
numer
block 0
numer
block 1
numer
block 2
numer
block 3
numer
block 1020
numer
block 1021
numer
block 1022
numer
block 1023
numer
Index Block = 4KB
block num =
4B

4KB 的盘块刚好可以存储下 1K 个盘块号作为索引,在读取文件时,操作系统先读取索引块,确定存储文件的所有盘块号,再去读取所有存储有文件数据的盘块,如下图所示:

0
1
2
3
4
5
6
7
8
9
10
3
12
13
14
15
16
17
18
20
21
22
23
24
25
26
27
28
29
30
31
file
start
end
file1
9
25
Directory
19
Index Block
Data Block

上图给出的索引是一级索引,索引块的层级只有一层,索引块中直接存储数据块的盘块号。

在实践中还有多级索引,其思路与 多级页表 一致。一级索引块中存储的是二级索引块的盘块号,最后一级索引块中才存储有数据块的盘块号。

一级索引块
二级索引块
三级索引块
数据块

混合索引

N 级索引有一个显著的缺点,就是对于非常小的文件,比如大小只有两三个盘块的文件,仍然需要使用一个完整的索引块来存储这些数据块的盘块号,这样索引块中的很多空间都没有使用,这导致盘块利用率非常的低。

还有一种混合索引的方式,可以解决上述提到的缺点,这种方式针对小文件或者大文件进行不同的处理。如果文件比较小的话,使用直接块即可,如果文件比较大的话,可以使用单级或多级索引。

UNIX 系统采用的就是这种方式,文件的 inode 结构如下图所示:

mode
owners(2)
timestamps(3)
size block count
direct blocks
single indirect
double indirect
triple indirect
data
data
data
data
data
data
data
data

其中 direct blocks 中存储的就是直接块的盘块号,如果文件的大小只有几个盘块,直接将数据块的盘块号存储在 direct blocks 中即可。

此外,混合索引中还包含不同层次的索引块。 single indirect 是一级索引,double indirect 是二级索引,triple indirect 是三级索引。 通过多级索引,可以尽量避免文件碎片的存在,增大磁盘块的利用率。