IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: 系统运维
UNIX系统支持多个进程共享打开的文件。在讨论dup函数前,我们需要讨论下共享。为此,我们将检查内核为所有I/O使用的数据结构。
接下来的描述是概念化的。它可能也可能不匹配于一个特定的实现。参考Bach[1986]对于System
V里的这些结构的讨论。McKusick等[1996]描述了4.4BSD里的这些结构。McKusick和Neville-Neil[2005]讨论了
FreeBSD 5.2。要看Solaris的类似讨论,参考Mauro和McDougall[2001]。
内核使用了三种数据结构来表示一个打开的文件,而它们之间的关系决定了一个进程在文件共享上对另一个进程的效果。
1、每个进程在进程表里都有一个项。这个项是一个打开文件描述符的表,我们可以把它看成一个向量,每个描述符对应一个项。与每个文件描述符相关的有:
a、文件描述符标志(close-on-exec;参考3.6节和3.14节。)
b、文件表项的一个指针。
2、内核为所有打开的文件维护了一个文件表。每个文件表项包含:
a、文件的文件状态标志,比如读、写、添加、同步和非阻塞;3.14节会讨论更多这方面内容;
b、当前文件偏移量;
c、文件的v-node表项的指针。
3、每个打开文件(或设备)有一个v-node结构,它包含关于文件类型和在文件上的操作的函数指针的信息。对于大多数文件,v-node同样包含了这个
文件的i-node。当文件被打开时这些信息从硬盘读入,以便所有与该文件相关的信息都可读。比如,i-node包含了文件的属主,文件的大小,文件在硬
盘上的真实数据块的指针,等等。(我们在4.14节深入描述典型UNIX文件系统时讨论更多关于i-nodes。)Linux没有v-node。相反,它
使用一个通用i-node结构(generic i-node
structure)。尽管实现不同,v-node与通用i-node在概念上是相同的。两者都指向一个特定文件系统的i-node结构。
我们会忽略一些不影响我们讨论的实现细节。比如,打开文件描述符的表可以存储在用户区域而不是进程表里。这些表可以用许多方式实现--它们不必是数组,也可以实现为结构的链表。这些实现细节都不会影响我们关于文件共享的讨论。
这三个表的排列方式自从UNIX系统的早期版本就已经存在了,而这种排列是文件在多个进程内共享的关键。当我们在本章末讨论文件共享的更多方式时,我们再回过来讨论这个排列。
v-node被发明用来支持在一个单一的计算机系统上支持多个文件系统类型。它由Peter Weinberger(贝尔实验室)和Bill
Joy(Sun Microsystems)独立发明。Sun称之为虚拟文件系统(Virtual File
System),而称i-node中文件系统无关的部分为v-node[Kleiman
1986]。v-node在各种厂商实现在传播开来,比如Sun的网络文件系统(Network File
System,NFS)。当NFS被加入时,Berkeley的第一个提供v-node的版本是4.3BSD Reno版本。
在SVR4,v-node代替了SVR3的文件系统无关i-node。Solaris从SVR4继承而来,因此也使用v-node。
与把数据结构分为v-node和i-node不同,Linux使用一个文件系统无关的i-node和一个文件系统有关的i-node。
如果两个独立的进程有相同的打开的文件,我们可以看到以下排列:两个进程有两个不同的进程表项,每个进程表项指向不同的文件表项,但这两个不同的文件表项指向相同的一个v-node表项。每个进程都有它自己的文件表项的原因是它们都有自己的文件当前偏移量。
有了这些数据结构,我们现在需要更深入地探讨执行我们已经讨论过的特定操作时会发生什么:
1、在每个写操作完成后,文件表项里的当前文件偏移量都根据写入的字节数增加。如果这导致当前文件偏移量超过当前文件尺寸,在i-node表里的当前文件尺寸会被设置成当前文件偏移量(比如,这个文件被扩展)。
2、如果文件用O_APPEND标志打开,一个对应的标志会设置给文件表项的文件状态标志中。每次一个写操作使用这个append标志应用到一个文件上
时,在文件表项的当前文件偏移量首先设置为从i-node表项中得到的当前文件尺寸。这强迫每个write都会添加到文件的当前结束位置。
3、如果文件使用lseek定位到它当前的文件末尾,所有发生的事情只有在文件表项时的当前文件偏移量被设置为从i-node表项中得到的当前文件尺寸。(注意这与以O_APPEND标志打开文件不同,我们会在3.11节看到。)
4、lseek函数仅修改在文件表项的当前文件偏移量。没有I/O发生。
多个描述符项指向同一个文件表项是可能的,正如我们将在3.12节讨论的dup函数。这同样在一个fork操作之后发生,这时父进程和子进程为每个打开的描述符共享同一个文件表项。
注意文件描述符标志和文件状态标志的范围的区别。前者仅应用于一个进程里的一个描述符,而后者应用于任何进程里指向给定文件表项的所有描述符。当我们在3.14节讨论fcntl函数时,将会看到如何获取和修改文件描述符标志和文件状态标志。