天天看點

CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向

10.8 共享檔案

基本概念:

核心三個相關的資料結構來表示打開的檔案:

  • 描述符表

    每個程序都有它獨立的描述符表,它的表項是由程序打開的檔案描述符來索引的。每個打開的描述符表項指向檔案表中的一個表項
  • 檔案表

    打開檔案的集合是由一張檔案表來表示的,所有的程序共享這張表。每個檔案表的表項組成包括目前檔案的位置、引用計數(即目前指向該表項的描述符表項數),以及一個指向v-node表中對應表項的指針。關閉一個描述符會減少相應的檔案表表項中的引用計數。核心不會删除這個檔案表表項,直到他的引用計數為零。
  • v-node表

    同檔案表一樣,所有程序共享這張v-node表。每個表項包含stat結構中的大多數資訊,包括st_mode和st_size成員。

示例1:

描述符1和描述符4通過不同的打開檔案表表項來引用兩個不同的檔案。沒有共享檔案,并且每個描述符對應一個不同的檔案。

CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向

示例2:

多個檔案描述符通過不同的檔案表表項來引用同一個檔案。

同一個filename調用open兩次,關鍵思想是每個描述符都有它自己的檔案位置,是以對不同的描述符的讀操作可以從檔案不同的位置擷取資料。

CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向

示例3:

父子程序共享檔案。假設調用fork之前,父程序如圖10-11所示的打開檔案。圖10-13展示了調用fork之後的情況。子程序有一個父程序描述符的副本。父子程序共享相同的打開檔案表集合,是以

共享相同的檔案位置

。一個很重要的結果就是在核心删除相應的檔案表表項之前,父子程序必須都關閉了它們的描述符。

CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向

相關代碼執行個體(對應示例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);
}
           

分析:

  1. fd1、fd2通過不同的檔案表表項來引用同一個檔案,每個描述符都有它自己的檔案位置,是以對不同的描述符的讀操作可以從檔案不同的位置擷取資料。
  2. fd1與fd2進入檔案時光标都在檔案開頭位置,fd2不會受fd1的影響

運作結果:

abcde.txt 的内容為:abcde。

CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向

相關代碼執行個體(對應示例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。

  1. int s = getpid() & 0x1

    :s等于父程序ID與0x1與的結果,若父程序ID為偶數(最後一位為0)則s=0;若父程序ID為奇數(最後一位為1)則s=1;
  2. sleep(s);和sleep(1-s);

    sleep函數可以将一個程序挂起一段指定的時間,若為sleep(1),該程序後執行;sleep(0)不影響程序執行
  3. 當父程序ID為偶數時,父程序中是sleep(0),子程序中是sleep(1)。是以父程序先執行,此時讀c2,目前光标在a後面,所有讀下一個字元 即c2=b;接下來執行子程序,因為這是父子程序共享檔案位置,是以讀子程序c2時,光标是在b後面的,是以c2=c (如第一次運作結果所示)
  4. 當父程序ID為奇數時,父程序中是sleep(1),子程序中是sleep(0),子程序先執行。(如下圖第二次運作結果所示)

運作結果:

abcde.txt 的内容為:abcde。12902、12903為目前程序ID号

CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向

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所示)

CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向

相關代碼執行個體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;
}
           

分析:

  1. fd1、fd2、fd3通過不同的檔案表表項來引用同一個檔案,每個描述符都有它自己的檔案位置,是以對不同的描述符的讀操作可以從檔案不同的位置擷取資料。而dup2(fd2, fd3);使fd2覆寫fd3的内容,此時fd2、fd3是同一個檔案表表項。
  2. fd1讀c1=a;fd2讀c2=a,fd3此時與fd2光标位置一樣都在a後面,使用fd3讀c3=b。

**運作結果:**abcde.txt 的内容為:abcde。

CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向

相關代碼執行個體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;
}

           

分析:

  1. fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);

    打開baz.txt檔案,有則打開并清空,無則建立;使用權限為可讀可寫。調用Write此時檔案内容為pqrs。
  2. fd3 = Open(fname, O_APPEND|O_WRONLY, 0);

    打開檔案 ,在檔案尾追加内容。調用Write此時檔案内容為pqrsjklmn。
  3. fd2 = dup(fd1);

    将fd2重定向到fd1位置,fd1的目前光标位置在s後面。調用Write,會覆寫fd3一部分内容,此時檔案内容為pqrswyzxn。
  4. Write(fd3, "ef", 2);

    調用Write在檔案為追加内容,此時檔案内容為pqrswyzxnef。

運作結果: 寫入baz.txt檔案中

CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向
CSAPP:第十章 共享檔案與I/O重定向 執行個體說明10.8 共享檔案10.9 I/O重定向

文中示例圖檔來源:https://www.cnblogs.com/wuchanming/p/4459286.html

繼續閱讀