linux系統檔案流、檔案描述符與進程間關係詳解

來源:互聯網
上載者:User
linux(unix)進程與檔案的關係錯綜複雜,本教程試圖詳細的闡述這個問題。包括:    1、linux多/單進程與多/單檔案對於檔案流和描述符在使用時的關聯情況及一些需要注意的問題。    2、fork,vfork流緩衝等對檔案操作的影響。  1 、linux 檔案系統結構首先補充一點基礎知識,瞭解一下linux檔案系統。如所示:                                                      圖1 磁碟,分區和檔案系統 應該明白,所示結構是硬碟中檔案存放方式的一種邏輯表現形式,與進程無關。對於其中一些術語,見下面的解釋。i節點:包含檔案/目錄的幾乎全部-適用於放置在硬碟上的,需要長久儲存的資訊。例如:檔案所有者,檔案類型,i節點號(存放在目錄塊中),主次裝置號,串連計數,訪問/修改時間,IO塊長,檔案位元組數等等。可以用stat函數(#include <sys/stat.h>)擷取相關i節點資訊資訊。   2 、簡單的進程與檔案關係 下面,我們瞭解一下單進程多檔案以及多進程單檔案間的關係,在不考慮fork(父子進程)的情況下,除了指派陳述式給我們帶來一些小麻煩以外,這個問題還是相當容易的。   2.1 、一個進程同時開啟多個檔案:                                 圖2 一個進程同時開啟2個不同檔案時核心資料結構      其中,v節點我們幾乎已經介紹過了,因為它除了包含i節點之外,自身的內容實在是不怎麼多,重點看一下檔案表吧。      對於檔案表,要注意,它並不是從在於硬碟中的東西,可以說,他是進程的一部分(可能是由作業系統核心負責維護,本人未考證(確實是由核心負責維護的,參見APUE->Section3.12),因為它到底是進程的還是核心的,對於我們要探討的問題無關緊要)。檔案表包括:  檔案狀態標誌:包含讀,寫,添寫,同步和非阻塞等各種檔案開啟/目前狀態。  當前檔案位移量:記錄檔案指標位置   V節點/I節點:檔案類型和對此檔案進行各種操作的函數的指標,這些資訊都是在開啟檔案時候由磁碟讀入記憶體的。可用fcntl函數(#include <fcntl.h>)修改檔案表內容。   2.2 、多個無關聯進程同時開啟一個檔案:                                     圖3 兩個進程同時開啟同一個檔案時核心資料結構    此時,2個檔案分別使用不同的檔案表,這說明不同進程間檔案狀態標誌,檔案位移量等都是獨立的。但他們共用同一個v節點表。對於這種結構的特性,理解起來因該是個輕鬆的事情。  3 、檔案描述符或流的複製對於檔案描述符或流的複製,很多情況我們會採用指派陳述式,下面瞭解一個賦值和dup的不同之處,dup函數複製檔案描述符後的核心資料結構:( 檔案流和檔案描述符的轉換見這裡)                  圖4 執行dup函數(#include <unistd.h>)複製檔案描述符後,核心資料結構。    此時,2個fd檔案標誌同時使用同一檔案表。   3.1 dup 與指派陳述式用於檔案描述符的區別  為了瞭解dup與指派陳述式用於檔案描述符的區別,請看如下程式。  程式描述:    開啟一個檔案描述符,分別適用dup和指派陳述式進行複製,複製之後,列印原始和被複製的檔案描述符id,看看是否具有相同的值,然後關閉檔案,測試關閉是否成功。程式樣本:#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h> int sys_err(char *str){    puts(str);    exit(0);} int main(void){    int p,q;     if((p=open("c_fid.c", O_RDONLY)) == -1)        sys_err("open error");    q = dup(p);    puts("dup:");    printf("file p,q fd is:%d %d\n", q, p);    printf("close file p ok?: %d\n", close(p));    printf("close file q ok?: %d\n", close(q));     if((p=open("c_fid.c", O_RDONLY)) == -1)        sys_err("open error");    q = p;    puts("=:");    printf("file p,q fd is:%d %d\n", q, p);    printf("close file p ok?: %d\n", close(p));    printf("close file q ok?: %d\n", close(q));     return 0;} 程式運行結果:dup:file p,q fd is:4 3   //檔案p,q使用不同的檔案描述符close file p ok?: 0close file q ok?: 0 //檔案關閉成功=:file p,q fd is:3 3 //簡單複製close file p ok?: 0close file q ok?: -1//關閉失敗,原因是此描述符已經被關閉了     由此證明,dup是產生一個新的檔案描述符id和指標,但是他們共用檔案表,效果4,這時,關閉一個檔案描述符,另外一個仍舊可用,檔案表並不會被釋放。而指派陳述式不同,它只是簡單的在另外一個變數中記錄原始檔案指標等,2個變數的檔案描述符相同,進程表項中並不產生新的項目。  3.2 、指派陳述式複製標準流。例:#include <stdio.h>#include <stdlib.h> int sys_err(char *str){    puts(str);    exit(0);} int main(void){    FILE *p,*q;     if((p=fopen("c_fid.c", "r")) == NULL)        sys_err("open error");    q = p;    printf("FILE p,q fd is:%d %d\n", fileno(q),fileno(p));    printf("close file p ok?: %d\n", fclose(p));    printf("close file q ok?: %d\n", fclose(q));     return 0;} 程式執行結果:FILE p,q fd is:3 3 //2個流共用同一個檔案描述符close file p ok?: 0*** glibc detected ***//2次關閉引起錯誤,造成程式崩潰。…………   4 、  引入fork 後進程與檔案關係以及流緩衝對檔案操作的影響 4.1 fork                                    圖5 使用fork之後,父進程、子進程之間對開啟檔案的共用   使用fork後,子進程會繼承父進程所開啟的檔案表,此時,父子進程使用同一個檔案表(可見,上面猜測檔案表由核心維護因該是正確的,因為檔案表並沒有被複製),這說明2個進程將共用檔案狀態,檔案位移量等資訊。如果使用close關閉一個進程中的檔案描述符,那麼另一個進程中,此描述符仍然有效,相應檔案表也不會被釋放。    需要注意的是,在使用c標準io進行檔案讀寫時,先結束的進程會將緩衝區內資料也計入檔案位移量的長度,對於相應檔案緩衝區類型和長度,可以使用setbuf,setvbuf(#include <stdio.h>)設定。程式樣本:#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <time.h> int main(void){    int pid;    FILE *p;    char buff[20]={0};    int test=-2;     if((p=fopen("/media/lin_space/soft/netbeans-6.5-ml-cpp-linux.sh", "r")) == NULL)    {//檔案大於4096位元組        puts("open error.");exit(0);    }       if((pid=fork()) < 0)    {        puts("fork error");        exit(0);    }    else if(pid == 0)    {          sleep(2);        test = ftell(p);//返回當前位移量        printf("\nchild - ftell is: %d\n", test);        if((buff[0] = fgetc(p)) != EOF)            printf("child - fgetc is: %c\n", buff[0]);        else            puts("child - fgetc error\n");        test = ftell(p);        printf("child - ftell is: %d\n", test);    }    else    {        test = ftell(p);        printf("\nparent - ftell is: %d\n", test);        if((buff[0] = fgetc(p)) != EOF)            printf("parent - fgetc is: %c\n", buff[0]);        else            puts("parent - fgetc error\n");        test = ftell(p);        printf("parent - ftell is: %d\n", test);    }     printf("parent and child - close file ok?: %d\n", fclose(p));    return 0;} 程式執行結果:parent - ftell is: 0parent - fgetc is: #parent - ftell is: 1parent and child - close file ok?: 0freec@freec-laptop:/media/lin_space/summa/apue/unit8$     //父進程結束child - ftell is: 4096     //子進程檔案位移量為4096,原因是檔案緩衝類型為全緩衝,緩衝區大小為4096child - fgetc is: achild - ftell is: 4097parent and child - close file ok?: 0     //檔案關閉成功  4.2 vfork    而對於vfork,雖然子進程運行於父進程的空間,但是子進程卻擁有自己的進程表項(包含進程pid,fd標誌,檔案指標等),所以,在其中一個進程結束後,另外一個進程的檔案描述符依舊有效,子進程得到的是父進程檔案描述符的副本。    但是vfork對於標準流,情況則不同,一個進程結束後(如果不是使用_exit()結束進程),則此進程結束時可能會沖洗流緩衝區,並且關閉流,對於是否這樣做,要取決於具體實現  fork與vfork的區別見這裡
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.