首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

3.10 文件共享

UNIX系统支持在不同进程间共享打开的文件。在介绍dup函数之前,先要说明这种共享。为此先介绍内核用于所有I/O的数据结构。

下面的说明是概念性的,与特定实现可能匹配,也可能不匹配。参阅Bach[1986]对系统V中相关数据结构的讨论。McKusick等人[1996]说明了4.4BSD中的相关数据结构。McKusick和Neville-Neil[2005] 70 对FreeBSD 5.2进行了介绍。对Solaris的类似讨论参见Marno和McDougall[2001]。

内核使用三种数据结构表示打开的文件,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。

        (1) 每个进程在进程表中都有一个记录项,记录项中包含有一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:

   (a) 文件描述符标志(close_on_exec,参见图3-1和3.14节)。

   (b) 指向一个文件表项的指针。

        (2) 内核为所有打开文件维持一张文件表。每个文件表项包含:

        (a) 文件状态标志(读、写、添写、同步和非阻塞等,关于这些标志的更多信息参见     3.14节)。

        (b) 当前文件偏移量。

        (c) 指向该文件v节点表项的指针。

         (3) 每个打开文件(或设备)都有一个v节点(v-node)结构。v节点包含了文件类型和对此文件进行各种操作的函数的指针。对于大多数文件,v节点还包含了该文件的i节点(i-node,索引节点)。这些信息是在打开文件时从磁盘上读入内存的,所以所有关于文件的信息都是快速可供使用的。例如,i节点包含了文件的所有者、文件长度、文件所在的设备、指向文件实际数据块在磁盘上所在位置的指针等等(4.14节较详细地说明了典型UNIX系统文件系统,并将更多地介绍i节点)。

Linux没有使用v节点,而是使用了通用i节点结构。虽然两种实现有所不同,但在概念上,v节点与i节点是一样的。两者都指向文件系统特有的i节点结构。

我们忽略了某些实现细节,但这并不影响我们的讨论。例如,打开文件描述符表可存放在用户空间,而非进程表中。这些表也可以用多种方式实现,不必一定是数组;例如,可将它们实现为结构的链接表。这些细节并不影响我们在文件共享方面的讨论。

图3-1显示了一个进程的三张表之间的关系。该进程有两个不同的打开文件:一个文件打开为标准输入(文件描述符0),另一个打开为标准输出(文件描述符为1)。从UNIX系统的早期版本[Thompson 1978]以来,这三张表之间的基本关系一直保持至今。这种安排对于在不同进程之间共享文件的方式非常重要。在以后的章节中涉及其他文件共享方式时还会回到这张图上来。创建v节点结构的目的是对在一个计算机系统上的多文件系统类型提供支持。这一工作是由Peter Weinberger (贝尔实验室)和Bill Joy(Sun公司)分别独立完成的。Sun称此种文件系统为虚拟文件系统(Virtual File System),称与文件系统类型无关的i节点部分为v节点[Kleiman 1986]。当各个制造商的实现增加了对Sun的网络文件系统(NFS)的支持时,它们都广泛采用了v节点结构。在BSD系列中首先提供v节点的是4.3BSD Reno版本,其中加入了NFS。在SVR4中,v节点代换了SVR3中与文件系统类型无关的i节点结构。Solaris是从SVR4发展而来的,它也使用v节点。Linux没有将相关数据结构分为i节点和v节点,而是采用了一个独立于文件系统的i节点和一个依赖于文件系统的i节点。

如果两个独立进程各自打开了同一个文件,则有图3-2中所示的安排。我们假定第一个进程在文件描述符3上打开该文件,而另一个进程则在文件描述符4上打开该文件。打开该文件的每个进程都得到一个文件表项,但对一个给定的文件只有一个v节点表项。每个进程都有自己的文件表项的一个理由是:这种安排使每个进程都有它自己的对该文件的当前偏移量。

给出了这些数据结构后,现在对前面所述的操作作进一步说明。

        • 在完成每个write后,在文件表项中的当前文件偏移量即增加所写的字节数。如果这使

        当前文件偏移量超过了当前文件长度,则在i节点表项中的当前文件长度被设置为当前文件偏移量(也就是该文件加长了)。

        • 如果用O_APPEND标志打开了一个文件,则相应标志也被设置到文件表项的文件状态标志中。每次对这种具有添写标志的文件执行写操作时,在文件表项中的当前文件偏移量首先被设置为i节点表项中的文件长度。这就使得每次写的数据都添加到文件的当前尾端处。        • 若一个文件用lseek定位到文件当前的尾端,则文件表项中的当前文件偏移量被设置为i 节点表项中的当前文件长度。(注意,这与用O_APPEND标志打开文件是不同的,详见           3.11 节。)

        • lseek函数只修改文件表项中的当前文件偏移量,没有进行任何I/O操作。

 可能有多个文件描述符项指向同一文件表项。在3.12节中讨论dup函数时,我们就能看到这一点。在fork后也会发生同样的情况,此时父、子进程对于每一个打开文件描述符共享同一个文件表项(见8.3节)。

注意,文件描述符标志和文件状态标志在作用域方面的区别,前者只用于一个进程的一个描述符,而后者则适用于指向该给定文件表项的任何进程中的所有描述符。在3.14节说明fcntl函数时,我们将会了解如何获取和修改文件描述符标志和文件状态标志。

本节上面所述的一切对于多个进程读同一文件都能正确工作。每个进程都有它自己的文件表项,其中也有它自己的当前文件偏移量。但是,当多个进程写同一个文件时,则可能产生预期不到的结果。为了说明如何避免这种情况,需要理解原子操作的概念。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论