关于进程级打开文件表与系统级打开文件表
进程级打开文件表与系统级打开文件表
当一个进程调用 open()
系统调用打开一个文件时,操作系统会进行一系列操作,并涉及到这两个重要的文件表。这两个表协同工作,实现了文件的高效管理和共享。
核心思想:
-
系统级打开文件表(或称“全系统打开文件表”、“文件表”):维护系统中所有当前被打开的文件实例的信息。无论文件被多少个进程打开,每个被打开的文件实例(对应一个磁盘上的文件实体)在这个表中只有一个条目。
-
进程级打开文件表(或称“用户文件描述符表”、“文件描述符表”):每个进程都有自己独立的这个表,它维护了该进程当前打开的所有文件的信息。表中的条目通常是文件描述符(一个整数),并指向系统级打开文件表中的相应条目。
1. 进程级打开文件表 (Per-process Open File Table)
-
作用:维护了当前进程所打开的所有文件的信息。它将文件描述符(一个整数)映射到系统级打开文件表中的具体条目。
-
别名:用户文件描述符表、文件描述符表。
-
结构:通常是一个数组,数组的索引就是文件描述符。每个数组元素是一个指向系统级打开文件表项的指针。
-
特点:
-
每个进程独有:一个进程一个表。
-
文件描述符:用户程序通过文件描述符来引用打开的文件。文件描述符是一个小的非负整数。
-
指向性:表中的每个条目都指向系统级打开文件表中的一个特定条目。
-
表格表示:
进程 A 的打开文件表
文件描述符 (fd) | 指向系统级文件表的指针 / 索引 |
---|---|
0 (标准输入) | -> 系统表项 X |
1 (标准输出) | -> 系统表项 Y |
2 (标准错误) | -> 系统表项 Z |
3 | -> 系统表项 F1 (文件A) |
4 | -> 系统表项 F2 (文件B) |
... | ... |
进程 B 的打开文件表
文件描述符 (fd) | 指向系统级文件表的指针 / 索引 |
---|---|
0 (标准输入) | -> 系统表项 X |
1 (标准输出) | -> 系统表项 Y |
2 (标准错误) | -> 系统表项 Z |
3 | -> 系统表项 F1 (文件A) |
4 | -> 系统表项 F3 (文件C) |
... | ... |
2. 系统级打开文件表 (System-wide Open File Table)
-
作用:维护了整个系统中所有当前被打开文件的实例信息。它包含了文件在磁盘上的实际位置、当前读写位置、访问模式、引用计数等重要信息。
-
别名:全系统打开文件表、文件表。
-
结构:通常是一个链表或数组,每个元素代表一个当前被打开的文件实例。
-
特点:
-
全局性:整个系统共享一个表。
-
文件实例唯一性:如果同一个文件被多个进程打开,或者同一个进程多次打开同一个文件,系统级打开文件表会创建不同的条目(每个
open()
调用都创建一个新的系统级条目,除非是复制文件描述符或父子进程继承)。 -
指向 FCB/i-node:每个系统级表项都包含一个指向文件控制块(FCB)或 i-node 的指针,从而能够访问文件的所有元数据和数据块信息。
-
引用计数:每个条目通常包含一个引用计数器,记录有多少个进程级文件描述符或多少个进程正在引用这个系统级条目。当引用计数为零时,该系统级条目才会被清除。
-
表格表示:
系统级打开文件表
表项索引 / 地址 | 指向 FCB/i-node 的指针 | 当前文件偏移量 (读写指针) | 打开模式 (读/写/追加) | 引用计数 | 其他信息 (如状态标志) |
---|---|---|---|---|---|
F1 | -> i-node 100 (文件A) | 1000 | 读写 (RW) | 2 | Non-blocking |
F2 | -> i-node 200 (文件B) | 500 | 只读 (R) | 1 | Async |
F3 | -> i-node 300 (文件C) | 0 | 写 (W) | 1 | |
X (标准输入) | -> i-node of stdin | ... | 只读 (R) | >1 | |
Y (标准输出) | -> i-node of stdout | ... | 写 (W) | >1 | |
Z (标准错误) | -> i-node of stderr | ... | 写 (W) | >1 | |
... | ... | ... | ... | ... | ... |
说明:
-
文件A:假设被进程A和进程B同时打开(或者进程A打开了两次),那么在系统级打开文件表中只有一个对应
i-node 100
的F1
条目,但其引用计数是2。 -
文件B:只被进程A打开。
-
文件C:只被进程B打开。
-
标准I/O:标准输入、输出、错误是系统启动时自动为每个进程打开的特殊文件。
3. 两者之间的关系和协同工作
-
open()
系统调用:-
当进程调用
open("fileA", mode)
时:-
操作系统首先根据文件名查找对应的 FCB/i-node。
-
如果 FCB/i-node 存在且权限允许,操作系统会在系统级打开文件表中创建一个新的条目(如果这个文件是第一次被这个进程以这个模式打开,或者是一个新的
open()
调用)。这个条目记录了文件的当前读写位置、打开模式等。 -
然后,操作系统在当前进程的进程级打开文件表中找一个空闲的位置,将该位置的指针指向新创建的系统级条目,并返回该位置的索引作为文件描述符给用户进程。
-
-
-
read()
/write()
系统调用:-
当进程调用
read(fd, buffer, count)
时:-
操作系统根据进程的
fd
(文件描述符)在进程级打开文件表中找到对应的指针。 -
通过该指针,访问系统级打开文件表中对应的条目。
-
从系统级条目中获取文件的当前读写位置和指向FCB/i-node的指针。
-
然后通过 FCB/i-node 中的物理地址信息,在磁盘上读写数据。
-
读写完成后,更新系统级条目中的当前读写位置。
-
-
-
close()
系统调用:-
当进程调用
close(fd)
时:-
操作系统清除进程级打开文件表中
fd
对应的条目。 -
并将系统级打开文件表中对应条目的引用计数减一。
-
如果引用计数变为零,则该系统级条目被清除,表示该文件实例已在整个系统范围内不再被任何进程引用。
-
-
4. 为什么要引入两级表?
这种两级结构提供了以下重要优势:
-
实现文件共享和独立访问:
-
多个进程可以打开同一个文件。每个进程在自己的进程级表中都有一个文件描述符,指向系统级表中的同一个或不同的条目。
-
如果不同进程独立打开同一个文件,它们会在系统级打开文件表中拥有各自独立的条目,因此它们可以拥有独立的读写指针。
-
如果父子进程通过
fork()
继承文件描述符,它们会指向同一个系统级表条目,从而共享读写指针。
-
-
提高效率和简化管理:
-
系统级表集中管理文件的全局状态信息(如读写指针、锁、引用计数),避免了每个进程都复制这些信息。
-
文件控制块(FCB/i-node)只存储文件本身的永久性属性(大小、权限、物理地址等),与打开状态分离,保持了简洁。
-
-
内存保护:进程只能通过文件描述符访问自己进程级表中的条目,不能直接访问系统级表,提供了安全保障。
-
支持文件重定向:文件描述符是一个抽象的整数,可以很容易地被重定向到不同的文件或设备。