linux文件I/O之文件共享
1. 3种数据结构
在了解文件共享之前先熟悉3个数据结构:进程表项, 文件表项,v节点表项,它们用来表示一个打开的文件。
(1)进程表项:每个进程在进程表中有一个记录项, 记录项包含一张打开文件描述符表,文件描述符包含:
a. 文件描述符标志(close_on_exec)
b. 指向一个文件表项的指针
(2)文件表项:内核为所有打开的文件维持一张文件表,每个文件表项包含:
a. 文件状态标志(如O_RDONLY, O_RDWR, O_APPEND等)
b. 文件当前偏移量
c. 指向该文件v节点的指针
(3)v节点表项:每个打开的文件有一个v节点表项,包含:
a. 文件类型
b. 对该文件进行操作的各个函数的指针
c. i-node(包含文件长度、文件所有者、指向文件实际数据块在磁盘上所在位置的指针等信息)
这3种数据结构的相互关系如图所示:
2. 文件共享几种情况
(1) 执行open函数,都会创建一个新的文件表项,比如调用:
fd1 = open(path, oflags);
fd2 = open(path, oflags);
则fd1和fd2分别指向不同的文件表项,但给定的文件只有一个v节点表项,所以两个文件表项中的v节点指针会指向同一个v节点表项,3种数据结构的关系如图:
在不同进程中打开同一个文件情况是类似的,都会得到一个新文件表项,共享同一个v节点表项,3种数据结构关系如图:
(2)执行dup函数,或者fcntl函数(其中的F_DUPFD或F_DUPFD_CLOEXEC命令),会共享文件表项,比如调用:
fd1 = open(path, oflags);
fd2 = dup(fd1); //或者 fd2 = fcntl(fd1, F_DUPFD, 0);
fd1和fd2指向同一个文件表项,3种数据结构关系如图所示:
(3)执行fork函数,父进程中所有打开的文件描述符都被复制到子进程中,既然是复制文件描述符,那么跟执行dup的效果是一致的,父子进程会共享文件表项,例如父进程有3个不同的打开文件,fork建立子进程后,3个数据结构的关系如图所示如图所示:
3. 总结
只要是复制文件描述符的操作(如dup, fcntl的F_DUPFD,fork),都会共享文件表项;
同一个文件每执行一次open,都会生成新的文件表项,每个文件表项的当前文件偏移量是独立的,在此情况下向同一个文件写数据可能会有问题:
比如两次open同一个文件得到fd1和fd2,其文件偏移量均指向文件开头;先用fd1向文件写入一些数据,fd1文件表项中的偏移量会更新,但fd2文件表项中的偏移量不变。然后用fd2向文件写入数据,因为此时fd2文件表项的偏移量仍指向文件开头,所以写入的数据会把先前通过fd1写的数据覆盖。