UNIX系统对于文件的关联
UNIX系统支持在不同进程间共享打开文件。内核使用3中数据结构表示文件,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。
1. 每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述符表,每个描述符占用一项。与每个文件描述符相关联的是:
a. 文件描述标志
b. 指向一个文件表项的指针
2. 内核为所有打开文件维持一张文件表。每个文件表象包含:
a. 文件状态标志(读、写、添写、同步和非阻塞等)
b. 当前文件偏移量
c. 指向该文件 v 节点表项得指针
3. 每个打开文件(或设备)都有一个 v 节点(v-node)结构。v节点包含了文件类型和对此文件进行各种操作函数得指针。对于大多数文件,v 节点还包含了该文件的 i 节点(i-node 索引节点)。这些信息是在打开文件时从磁盘上读入内存的,所以,文件的所有相关信息都是随时可用的。
注意:Linux 没有使用 v 节点,而是使用了通用的 i 节点结构。虽然两种实现有所不同,但在概念上,v 节点与 i 节点是一样的。两者都指向文件系统特有的 i 节点结构。
如果用 O_APPEND 标志打开一个文件,则相应标志也被设置到文件表项的文件状态标志中。每次对这种具有追加写标志的文件执行写操作时,文件表项中的当前文件偏移量首先会被设置为 i 节点表项中的文件长度。这就使得每次写入的数据都追加到文件的当前尾端处。
lseek 函数只修改文件表项中的当前文件偏移量,不进行任何 I/O 操作。
注:文件描述符标志和文件状态标志在作用范围方面的区别,前者只用于一个进程的一个描述符,而后者则应用于指向该给定文件表项的任何进程中的所有描述符。
原子操作
当多个进程写同一文件时,则可能产生预想不到的结果。
任何要求多于一个函数调用的操都不是原子操作,因为在两个函数调用之间,内核有可能会临时挂起进程。
原子操作指的是由多步组成的一个操作。如果该操作原子地执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。
函数 preaad 和 pwrite:允许原子性地定位并执行 I/O
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
//返回值:读到的字节数,若已到文件尾,返回0;若出错,返回-1
ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
//返回值:若成功,返回已写的字节数;若出错,返回-1
调用 pread 相当于调用 lseek 后调用 read,但是 pread 又与这种顺序调用有下列重要区别:
调用 pread 时,无法中断其定位和读操作
不更新当前文件偏移量