10.8 共享檔案
基本概念:
核心三個相關的資料結構來表示打開的檔案:
-
每個程序都有它獨立的描述符表,它的表項是由程序打開的檔案描述符來索引的。每個打開的描述符表項指向檔案表中的一個表項描述符表
-
打開檔案的集合是由一張檔案表來表示的,所有的程序共享這張表。每個檔案表的表項組成包括目前檔案的位置、引用計數(即目前指向該表項的描述符表項數),以及一個指向v-node表中對應表項的指針。關閉一個描述符會減少相應的檔案表表項中的引用計數。核心不會删除這個檔案表表項,直到他的引用計數為零。檔案表
-
同檔案表一樣,所有程序共享這張v-node表。每個表項包含stat結構中的大多數資訊,包括st_mode和st_size成員。v-node表
示例1:
描述符1和描述符4通過不同的打開檔案表表項來引用兩個不同的檔案。沒有共享檔案,并且每個描述符對應一個不同的檔案。

示例2:
多個檔案描述符通過不同的檔案表表項來引用同一個檔案。
同一個filename調用open兩次,關鍵思想是每個描述符都有它自己的檔案位置,是以對不同的描述符的讀操作可以從檔案不同的位置擷取資料。
示例3:
父子程序共享檔案。假設調用fork之前,父程序如圖10-11所示的打開檔案。圖10-13展示了調用fork之後的情況。子程序有一個父程序描述符的副本。父子程序共享相同的打開檔案表集合,是以
共享相同的檔案位置
。一個很重要的結果就是在核心删除相應的檔案表表項之前,父子程序必須都關閉了它們的描述符。
相關代碼執行個體(對應示例2)
#include "csapp.h"
int main()
{
int fd1,fd2;
char c;
fd1=Open("abcde.txt",O_RDONLY,0);
fd2=Open("abcde.txt",O_RDONLY,0);
Read(fd1,&c,1);
printf("c=%c\n",c);
Read(fd2,&c,1);
printf("c=%c\n",c);
exit(0);
}
分析:
- fd1、fd2通過不同的檔案表表項來引用同一個檔案,每個描述符都有它自己的檔案位置,是以對不同的描述符的讀操作可以從檔案不同的位置擷取資料。
- fd1與fd2進入檔案時光标都在檔案開頭位置,fd2不會受fd1的影響
運作結果:
abcde.txt 的内容為:abcde。
相關代碼執行個體(對應示例3)
#include "csapp.h"
int main(int argc, char *argv[])
{
int fd1;
int s = getpid() & 0x1;
char c1, c2;
char *fname = argv[1];
fd1 = Open(fname, O_RDONLY, 0);
Read(fd1, &c1, 1);
if (fork()) {
/* Parent */
sleep(s);
Read(fd1, &c2, 1);
printf("%d\n",getpid());
printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
} else {
/* Child */
sleep(1-s);
Read(fd1, &c2, 1);
printf("%d\n",getpid());
printf("Child: c1 = %c, c2 = %c\n", c1, c2);
}
return 0;
}
分析:父子程序共享檔案
父程序中fork傳回子程序的pid(非零),子程序中fork傳回0.
在調用子程序之前,父程序已經從 abcde.txt 讀了一個字元 即c1=a。
-
:s等于父程序ID與0x1與的結果,若父程序ID為偶數(最後一位為0)則s=0;若父程序ID為奇數(最後一位為1)則s=1;int s = getpid() & 0x1
-
sleep函數可以将一個程序挂起一段指定的時間,若為sleep(1),該程序後執行;sleep(0)不影響程序執行sleep(s);和sleep(1-s);
- 當父程序ID為偶數時,父程序中是sleep(0),子程序中是sleep(1)。是以父程序先執行,此時讀c2,目前光标在a後面,所有讀下一個字元 即c2=b;接下來執行子程序,因為這是父子程序共享檔案位置,是以讀子程序c2時,光标是在b後面的,是以c2=c (如第一次運作結果所示)
- 當父程序ID為奇數時,父程序中是sleep(1),子程序中是sleep(0),子程序先執行。(如下圖第二次運作結果所示)
運作結果:
abcde.txt 的内容為:abcde。12902、12903為目前程序ID号
10.9 I/O重定向
dup2函數
#include <unistd.h>
int dup2(int oldfd,int newfd);
傳回:若成功則傳回為非負的描述符,若出錯則為-1.
函數說明: dup2函數複制描述符表表項oldfd到描述符表表項newfd,覆寫描述符表表項newfd以前的内容。如果newfd已經打開了,dup2會在複制oldfd之前關閉newfd。
調用dup2(4,1)的示例圖(調用前如圖10-11所示)
相關代碼執行個體2
#include "csapp.h"
int main(int argc, char *argv[])//abcde.txt
{
int fd1, fd2, fd3;
char c1, c2, c3;
char *fname = argv[1];
fd1 = Open(fname, O_RDONLY, 0);
fd2 = Open(fname, O_RDONLY, 0);
fd3 = Open(fname, O_RDONLY, 0);
dup2(fd2, fd3);
Read(fd1, &c1, 1);
Read(fd2, &c2, 1);
Read(fd3, &c3, 1);
printf("c1 = %c, c2 = %c, c3 = %c\n", c1, c2, c3);
Close(fd1);
Close(fd2);
Close(fd3);
return 0;
}
分析:
- fd1、fd2、fd3通過不同的檔案表表項來引用同一個檔案,每個描述符都有它自己的檔案位置,是以對不同的描述符的讀操作可以從檔案不同的位置擷取資料。而dup2(fd2, fd3);使fd2覆寫fd3的内容,此時fd2、fd3是同一個檔案表表項。
- fd1讀c1=a;fd2讀c2=a,fd3此時與fd2光标位置一樣都在a後面,使用fd3讀c3=b。
**運作結果:**abcde.txt 的内容為:abcde。
相關代碼執行個體2
#include "csapp.h"
int main(int argc, char *argv[])
{
int fd1, fd2, fd3;
char *fname = argv[1];
fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
Write(fd1, "pqrs", 4);
fd3 = Open(fname, O_APPEND|O_WRONLY, 0);
Write(fd3, "jklmn", 5);
fd2 = dup(fd1); /* Allocates new descriptor */
Write(fd2, "wxyz", 4);
Write(fd3, "ef", 2);
Close(fd1);
Close(fd2);
Close(fd3);
return 0;
}
分析:
-
打開baz.txt檔案,有則打開并清空,無則建立;使用權限為可讀可寫。調用Write此時檔案内容為pqrs。fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
-
打開檔案 ,在檔案尾追加内容。調用Write此時檔案内容為pqrsjklmn。fd3 = Open(fname, O_APPEND|O_WRONLY, 0);
-
将fd2重定向到fd1位置,fd1的目前光标位置在s後面。調用Write,會覆寫fd3一部分内容,此時檔案内容為pqrswyzxn。fd2 = dup(fd1);
-
調用Write在檔案為追加内容,此時檔案内容為pqrswyzxnef。Write(fd3, "ef", 2);
運作結果: 寫入baz.txt檔案中
文中示例圖檔來源:https://www.cnblogs.com/wuchanming/p/4459286.html