2012-07-31 41 views
28

在「Unix環境下的高級編程」,第2版,由W. Richard Stevens編寫。任何人都可以在fork()之後解釋有關'文件描述符'的簡單描述嗎?

第8.3節fork函數。

這裏的描述:

認爲家長和孩子共享同一個文件偏移量是很重要的。

考慮一個過程,分叉一個孩子,然後等待孩子完成。假定這兩個進程都是作爲正常處理的一部分寫入標準輸出。如果父級的標準輸出被重定向(可能是由shell),那麼當子級寫入標準輸出時,父級的文件偏移量必須由子級更新。

[1。這是什麼意思?例如,如果父級的std輸出被重定向到'file1',那麼孩子在寫入後應該更新哪些內容?父母的原始標準輸出偏移或重定向輸出(即file1)偏移?不能是後來的吧?]

[2。更新如何完成?由孩子明確地,由操作系統隱式地由文件描述符本身?在fork之後,我認爲父母和孩子走自己的路,並擁有自己的文件描述符COPY。那麼孩子如何更新父母側的偏移?]

在這種情況下,孩子可以寫入標準輸出,而父母正在等待它;在孩子完成後,父母可以繼續寫入標準輸出,知道其輸出將被附加到孩子寫的任何內容。如果父母和孩子沒有共享相同的文件偏移量,這種類型的交互將更加難以完成,並且需要父母採取明確的行動。

如果父項和子項都寫入相同的描述符,而沒有任何形式的同步,比如讓父級等待子級,則它們的輸出將被混合(假設它是在fork之前打開的描述符)。雖然這是可能的,但這不是正常的操作模式。

在fork之後處理描述符有兩種正常情況。

  1. 父母等待孩子完成。在這種情況下,父母不需要對其描述符做任何事情。當孩子終止時,孩子讀取或寫入的任何共享描述符將相應地更新其文件偏移量。

  2. 父母和孩子都走自己的路。在這裏,在fork之後,parent關閉了它不需要的描述符,並且這個孩子做了同樣的事情。這樣,既不干擾對方的開放描述符。這種情形往往是與網絡服務器的情況。」

[3.當調用fork()的,我所不解的是,孩子得到了什麼樣的父母,文件描述符的副本在這種情況下,和做它的事情。如果有任何偏移變化文件描述符父母子女分享,那隻能是因爲描述符記得偏移本身。我說得對不對?]

對不起我是一種新的概念。

任何幫助?謝謝。

回答

67

是很重要的文件描述符,這是該進程在其讀取和寫入調用來識別文件使用一個小的整數,而文件描述,這是在內核結構來區分。文件偏移量是文件描述的一部分。它生活在內核中。

舉個例子,讓我們用這個程序:

#include <unistd.h> 
#include <fcntl.h> 
#include <sys/wait.h> 

int main(void) 
{ 
    int fd; 

    fd = open("output", O_CREAT|O_TRUNC|O_WRONLY, 0666); 

    if(!fork()) { 
     /* child */ 
     write(fd, "hello ", 6); 
     _exit(0); 
    } else { 
     /* parent */ 
     int status; 

     wait(&status); 
     write(fd, "world\n", 6); 
    } 
} 

(所有錯誤檢查已被省略)

如果我們編譯這個程序,把它hello,像這樣運行它:

./hello 

這裏發生了什麼:

程序開放s output文件,創建它,如果它不存在或截斷它爲零大小,如果它確實存在。內核創建一個文件描述(在Linux內核中這是一個struct file),並將其與調用進程的文件描述符(該進程的文件描述符表中尚未使用的最低非負整數)相關聯。文件描述符被返回並在程序中分配給fd。爲了說明起見,假設fd是3.

該程序執行fork()。新的子進程獲取其父文件描述符表的副本,但不會複製文件描述。兩個進程文件表中的條目編號3指向相同的struct file

父進程等待子進程寫入。孩子的寫作導致"hello world\n"的前半部分存儲在文件中,並將文件偏移量提前6.文件偏移量爲struct file

孩子退出,父母的wait()完成,父母使用fd 3進行書寫,該fd 3仍然與相同的文件描述相關聯,文件描述的文件偏移由孩子的write()更新。因此,郵件的後半部分在之後被存儲爲第一部分,而不是覆蓋它,因爲如果父文件偏移量爲零,則會執行該操作,如果文件描述未被共享,則會發生這種情況。

最後父節點退出,內核看到struct file不再被使用並釋放它。

+1

艾倫,解釋很棒!感謝您花時間爲新手解釋它的細節。 – user1559625 2012-07-31 06:55:44

+1

@AlanCurry如果我不在父程序中使用wait()會怎麼樣?作爲父進程退出時,文件描述是免費的嗎? – Nmzzz 2014-02-15 08:10:23

+0

有幫助!謝謝! – Kross 2016-10-28 13:04:18

3

在本書的同一節中,有一個圖表顯示打開文件時出現的三個表格。

用戶filedescriptor表(進程表條目的一部分),filetable和inode表(v節點表)。 現在,filedescriptor(它是文件描述符表的索引)條目指向指向inode表條目的文件表條目。
現在文件偏移量(發生下一次讀/寫的位置)在文件表中。

所以說,你在父母開了一個文件,這意味着它是有一個描述符,文件 表條目和索引節點引用過。
現在,當您創建一個孩子時,將爲該孩子複製文件描述符表。 因此,文件表項(對於該打開的描述符)中的引用計數增加了,這意味着現在對同一個文件表項有兩個引用。

此描述符現在在父級和子級都可用,指向相同的文件表項,因此共享偏移量。 現在有這樣的背景,讓我們看看你的問題,

  1. 是什麼意思?例如,如果父級的std輸出被重定向到'file1',那麼孩子在寫入後應該更新哪些內容?父母的原始標準輸出偏移量還是 重定向輸出(即file1)偏移量?不能是後來的吧?]

孩子顯然不需要更新任何東西。本書的作者正在試圖
告訴我們,假設父母的標準輸出被重定向到一個文件併產生了一個分叉調用。在那之後,父母就開始流水了。所以描述符現在被複制了,也就是文件偏移量也被共享了。現在,無論何時現在孩子將任何內容寫入標準輸出,寫入的數據都保存在重定向的文件中。 寫入調用會自動增加偏移量。

現在說孩子退出。 因此,父母等待並寫出標準輸出(這是 重定向)。現在父級的寫入調用的輸出將放置在 - >數據之後,該數據由孩子寫入。爲什麼 - >因爲在孩子寫完之後現在偏移量的值已經改變了。

Parent () 
    { 
    open a file for writing, that is get the 
    descriptor(say fd); 
    close(1);//Closing stdout 
    dup(fd); //Now writing to stdout means writing to the file 
    close(fd) 
     //Create a child that is do a fork call. 
    ret = fork(); 
    if (0 == ret) 
    { 
     write(1, "Child", strlen("Child"); 
     exit .. 
    } 
     wait(); //Parent waits till child exit. 

     write(1, "Parent", strlen("Parent"); 
    exit .. 
} 

P1。看到上面的僞代碼,打開的文件包含的最終數據將是ChildParent。所以你可以看到,當孩子寫完後,文件偏移量已經改變了,並且這對於父母的寫入調用來說是可用的,因爲這是共享的。

2.如何完成更新?由孩子明確地,由操作系統隱式地由文件描述符本身?後叉,我認爲父母和孩子去了自己的方式和 有自己的文件描述符COPY。那麼孩子如何向母方更新偏移?]

Now I think the answer is clear-> by the system call that is by the OS. 

[3。當fork()被調用時,我所理解的就是這個孩子得到了一個父母擁有的副本,在這種情況下,文件描述符就是 ,並且做它的事情。如果任何偏移更改爲父級和子級共享的文件描述符,則只能由於描述符本身記住偏移量。我對嗎?]

這應該也很清楚。用戶文件表的項目指向文件表 表項(其中包含偏移量)。

換句話說,系統調用可以從描述符中獲取偏移量。

+0

「文件表條目中的引用計數(對於該打開的描述符)增加」。你能指出一個參考嗎?這意味着fork複製父內存不是淺拷貝。 – dashesy 2017-06-23 17:49:28