Linux系統程式設計之檔案IO

烏有先生ii發表於2021-11-08

前言

在學習C語言時,我們接觸過如fopen、fclose、fseek、fgets、fputs、fread、fwrite等函式,實際上,這些函式是對於底層系統呼叫的封裝。C預設會開啟三個輸入輸出流,分別是stdin,stdout,stderr。執行man stdin後,會展示如下描述:

   #include <stdio.h>
   extern FILE *stdin;
   extern FILE *stdout;
   extern FILE *stderr;

可以看到,這三個流型別都是FILE*,也就是說指向了某個檔案。實際上,以上三者分別對應的檔案為鍵盤、顯示器、顯示器。

那麼,作業系統是如何管理檔案,並進行檔案IO呢?

1. 檔案描述符及基本IO介面介紹

1.1 什麼是檔案描述符

在第一講中,我們知道了當程式被建立後,系統會給該程式分配對應的PCB,在Linux中,程式的PCB是task_stuct,裡面有一項files_struct——開啟檔案表。開啟檔案表的原始碼如下:

struct files_struct {

    atomic_t count; /* 共享該表的程式數 */

    rwlock_t file_lock; /* 保護以下的所有域,以免在tsk->alloc_lock中的巢狀*/

    int max_fds; /*當前檔案物件的最大數*/

    int max_fdset; /*當前檔案描述符的最大數*/

    int next_fd; /*已分配的檔案描述符加1*/

    struct file ** fd; /* 指向檔案物件指標陣列的指標 */

    fd_set *close_on_exec; /*指向執行exec( )時需要關閉的檔案描述符*/

    fd_set *open_fds; /*指向開啟檔案描述符的指標*/

    fd_set close_on_exec_init;/* 執行exec( )時需要關閉的檔案描述符的初值集合*/

    fd_set open_fds_init; /*檔案描述符的初值集合*/

    struct file * fd_array[32];/* 檔案物件指標的初始化陣列*/

};

程式是通過檔案描述符(file descriptors,簡稱fd)而不是檔名來訪問檔案的,檔案描述符是一個整數。

在開啟檔案表中,最重要的一項是fd_array[32],這是一個指標陣列,通常,fd_array包括32個檔案物件指標,如果程式開啟的檔案數目多於32,核心就分配一個新的、更大的檔案指標陣列,並將其地址存放在fd域中,核心同時也更新max_fds域的值。

每當開啟一個檔案時,系統就會分配fd_array中的某項,將其指向開啟的檔案結構體。從下圖我們可以看出,fd實際上就是fd_array的索引。只要有fd,就可以找到對應檔案的位置。

image-20210820120104359

1.2 基本IO介面

在認識返回值之前,需要先區分兩個概念: 系統呼叫和庫函式。 在使用者程式中,凡是與資源有關的操作(如儲存分配、進行I/O傳輸及管理檔案等),都通過系統呼叫方式向作業系統提出服務請求,並由作業系統代為完成。在執行系統 呼叫的過程中,作業系統會由使用者態進入到核心態。系統為了防止應用程式能隨意修改系統資料,只給使用者提供介面,使用者要使用,那就通過提供的介面來呼叫。

fopen、fclose、fread、fwrite 都是C標準庫當中的函式,我們稱之為庫函式(libc),而open close read write lseek 都屬於系統提供的介面,稱之為系統呼叫介面。實際上,庫函式往往是對系統呼叫介面的進一步封裝,方便程式設計師進行二次開發。

1.2.1 open/close介面

函式原型:

標頭檔案:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

介面1:int open(const char *pathname, int flags); :

介面2:int open(const char *pathname, int flags, mode_t mode);

介面3:int close(int fd);

如果開啟的檔案存在,則使用介面1,如果開啟的檔案不存在,則使用介面2。

pathname:待開啟或建立的檔案

flags:以何種方式開啟。開啟檔案時,可以傳入多個常量進行“或”運算。這些常量有:

​ 0_RDONLY:只讀開啟;O_WRONLY:只寫開啟;O_RDWR:讀寫開啟。這三個常量,必須指定且只能指定一個。

​ O_CREAT:若檔案不存在,則建立該檔案,需要使用mode選項,來指明新檔案的訪問許可權。

​ O_APPEND:追加寫入。

​ O_TRUNC:截斷檔案(清空檔案內容)

O_NONBLOCK :使用非阻塞方式讀寫裝置檔案,如果不新增,預設情況下讀寫為阻塞方式。

以上的選項是按照按位或的方式進行組合的,O_RDWR|O_CREAT|O_APPEND 意思是以讀寫的方式開啟檔案,如果檔案不存在則建立檔案,開啟檔案後寫入方式為追加寫入。

mode:當建立一個新檔案時,需要給檔案設定許可權,一般通過傳遞一個8進位制的數字。關於檔案許可權,請讀者自行查閱相關文章。

返回值:

​ 建立成功返回一個檔案描述符

​ 建立失敗返回-1。

1.2.2 read/wirte介面

ssize_t read(int fd, void *buf, size_t count);

fd:檔案描述符

buf:將檔案讀到buf指向的空間中

count:期望讀取的位元組數

ssize_t write(int fd, const void *buf, size_t count)

fd:檔案描述符

buf:將buf中的內容寫到檔案當中去

count:期望寫入的位元組數,size_t被定義為unsigned long

返回值:返回讀出或者寫入的位元組數。需要注意的是read和write的返回值都是有符號數,ssize_t被定義為long,出錯的時候返回-1。有趣的是返回一個-1的可能性使得讀到或者寫入的最大值減小了一半。

通過下例來感受一下上面幾個介面的使用:

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    
char buff[1024];    
int main()    
{    
  int fd = open("wrfile",O_RDWR|O_CREAT|O_APPEND,0644);    
  if(fd == -1)    
  {    
    return 1;    
  }    
  else{    
    const char* str = "Hello world\n";                                                                                                                  
    strcpy(buff,str);    
    write(fd,buff,strlen(buff));    
    printf("%d\n",fd);    
    close(fd);    
  }    
    
  return 0;    
} 

執行該段程式碼後,可以看到如下輸出結果

image-20210820191541316

第一,開啟檔案後,將返回wrfile的fd,執行write函式,會將buff中的內容寫入到wrfile中。

第二,輸出wrfile的內容,發現如果只執行一次,輸出一行“Hello world”,如果執行兩次,輸出兩行“Hello world”,這是因為我們是以追加的方式開啟的檔案。

第三,開啟檔案後,返回的fd號碼是3。為什麼會是3?這就與檔案描述符的分配規則有關。

1.3 檔案描述符的分配規則

根據我們在1.1中知道的,fd是fd_array[ ]的索引。通常,陣列的第一個元素(索引為0)是程式的標準輸入檔案,陣列的第二個元素(索引為1)是程式的標準輸出檔案,陣列的第三個元素(索引為2)是程式的標準錯誤檔案,分別對應的鍵盤,顯示器,顯示器。在Linux中,萬物皆檔案,因此各種外設也會當做檔案進行處理。

是不是瞬間明白了為什麼使用者自己開啟第一個檔案的時候分配的fd是3而不是0?是的,這是因為對於任何程式,標準輸入檔案、標準輸出檔案和標準錯誤檔案會被預設開啟。當再次分配的時候,作業系統會採用最小未分配原則——即先分配當前fd_array中未被使用的最小索引。

如果我們先關閉了fd為1的檔案,會是什麼情況呢?請看下例:

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    
    
int main()    
{    
  close(1);    
  int fd = open("./myfile",O_RDWR|O_CREAT,0644);    
  if(-1 == fd)    
  {    
    return -1;    
  }else{    
      printf("The fd is : %d\n",fd);    
      fflush(stdout);                                                                                                                   
      close(fd);    
  }    
  return 0;    
} 

執行該段程式碼後,結果如下:

image-20210821114016940

執行./test後,並沒有在螢幕上列印出結果。但是當我們檢視myfile檔案中的內容時發現,原來是內容都輸出在了myfile檔案中。我們可以看到,開啟myfile的fd為1,這是因為我們之前關閉了fd為1的檔案,當開啟新的檔案時,系統會分配最小未使用的fd——1。

為什麼執行printf後內容會輸出到myfile中而不是螢幕上,這就需要提到輸出重定向。

1.4 輸入重定向與輸出重定向的本質

1.4.1重定向的原理

printf是格式化輸出函式,當呼叫printf時,資料會被預設輸出到緩衝區中,當換行符或者緩衝區滿時,會將資料重新整理到stdout中。stdout是標準輸出裝置,其fd預設為1。

在上例中,關閉fd為1的檔案後,當開啟新的檔案,會給其分配最小未使用的fd。此時執行printf函式,會將資料輸出到磁碟檔案myfile中,我們將這種現象稱為輸出重定向。常見的重定向有:>, >>, <,分別為輸出重定向,追加重定向,輸入重定向。重定向的原理如下圖所示:

image-20210821121610540

需要注意的是,我們在執行完printf後,使用了fflush函式來重新整理緩衝區。如果不使用這個函式,當執行./test後,我們會發現資料並未輸出到myfile中,這就需要提到緩衝區。

在預設情況下,stdout是行緩衝的,他的輸出會放在一個buffer裡面,只有到換行的時候,才會輸出到螢幕,stderro是無緩衝,會直接進行io操作。而平時使用的磁碟檔案是全緩衝(或稱滿緩衝)的,只有緩衝區滿的時候才會將緩衝區裡面的內容重新整理。當關閉stdout,開啟myfile後,會變成全緩衝,因此需要我們執行fflush強制重新整理緩衝區。緩衝區的型別如下:

img

需要注意的是,這裡的緩衝區指的是c程式中的使用者緩衝區,而不是核心緩衝區!

1.4.2 重定向在命令列的應用

如下圖所示,當執行cat指令後,myfile中的內容會輸出到螢幕上。當我們再次執行cat myfile,並使用>進行輸出重定向後,可以看出myfile中的內容輸出到了pfile中,當使用>>後,會發現pfile中有兩行資料,這就是追加重定向,即再原來檔案的末尾繼續輸出。

image-20210821164318098

1.4.3 dup2系統呼叫

如果我們想在IO時進行重定向操作,難道每次都需要先close一個檔案,再申請對應的fd嗎?這樣無疑增加了編碼的複雜程度,因此,如果想進行重定向操作,推薦使用dup2系統呼叫。

介面描述:

int dup2(int oldfd, int newfd);

該介面用來複制新的檔案描述符。通俗地說,fd_array[ ]中存放著指向若干開啟的檔案結構體file,比如此時某個檔案的fd為3,我們想對這個檔案進行輸出重定向,讓本應該輸出到fd為3的檔案中的資料輸出到螢幕上,就可以通過呼叫dup2(3,1)。原理是把fd為3的檔案結構體指標複製到fd為1的單元中,這樣3和1都指向了同一個檔案結構體。

示例如下:

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    
    
int main()    
{    
  int fd = open("./myfile",O_RDWR|O_CREAT,0644);    
  if(-1 == fd)    
  {    
    return -1;    
  }else{    
      printf("Hello world!\n");    
      dup2(fd,1);    
      close(fd);    
      printf("The fd is : %d\n",fd);    
      fflush(stdout);                                                                                                                                   
  }    
  return 0;    
} 

輸出結果如下:

image-20210822105214810

在程式中,有兩處printf函式,當執行./test後,螢幕上只列印出一行"Hello world!",而第二行的資料列印到了myfile中。我們明明已經對新開啟的檔案執行了close(fd),為什麼資料還是會列印到myfile中?

這是因為呼叫dup2的時候,將oldfd中的值複製到了newfd中。

注意複製的是陣列下標為fd中儲存的指標值而不是fd本身!若newfd指向的檔案已經被開啟,會先將其關閉。若newfd等於oldfd,就不關閉newfd,newfd和oldfd共同指向一份檔案。

1.5 fd與C庫中FILE的關係

C庫中的函式本質上是對系統呼叫的封裝,所以本質上,所有對於檔案的操作都是通過檔案描述符fd來實現的。那麼,C庫的FILE中一定有對應檔案的fd

2. 檔案系統

2.1 磁碟簡介

傳統的硬碟盤結構是像下面這個樣子的,它有一個或多個碟片,用於儲存資料。中間有一個主軸,所有的碟片都繞著這個主軸轉動。一個組合臂上面有多個磁頭臂,每個磁頭臂上面都有一個磁頭,負責讀寫資料。

image-20210822121019263

每個磁軌劃分為若干個弧段,每個弧段就是一個扇區 (Sector),是硬碟的最小儲存單位。每個扇區儲存512位元組(相當於0.5KB)。

作業系統讀取硬碟的時候,不會一個個扇區地讀取,因為這樣效率太低,而是一次性連續讀取多個扇區,即一次性讀取一個”塊”(block)。這種由多個扇區組成的”塊”,是檔案存取的最小單位。”塊”的大小,最常見的是4KB,即連續八個sector組成一個block。一個block的大小是由格式化的時候確定的,並且不可以更改。

2.2 inode(索引結點)

輸入ls -l指令後,我們能看到如下內容

image-20210822151332933

對於一個檔案而言,一個檔案= 檔案屬性+檔案內容。圖中所標識的部分就是檔案的各種屬性,那這些屬性是儲存在哪裡?又是怎樣儲存的呢?檔案的資料又存放在哪裡呢?

image-20210822153358985

上圖是一個簡易的磁碟系統分割槽圖,一般來說,在一個檔案系統內,一般將磁碟分為如下幾個區域:

超級塊:裡面存放該檔案系統本身的結構資訊,如bolok 和 inode(馬上會講)的總量, 未使用的block和inode的數量,一個block和inode的大小等。

block點陣圖:先來看一下點陣圖的結構

image-20210822154331571

block點陣圖有點像是一個超大型陣列,每個位元位所在位置可以看成陣列下標,而這個“下標”就是對應的block號,0代表該塊未分配,1代表該塊已經被分配。如在上圖中,表示9號塊已經分配。每分配一個block,要將對應位置的Bitmap置為1。

inode表:inode中存放的是一個檔案的元資訊,一般來說有以下資訊:

  • 該檔案的inode號,用來標識一個inode,而每個inode對應一個檔案,Linux系統內部不使用檔名,而使用inode號碼來識別檔案。對於系統來說,檔名只是inode號碼便於識別的別稱或者綽號。

    檢視inode號碼的指令:ls -i

    image-20210822173031218

  • 檔案的位元組數

  • 檔案擁有者的User ID ,所屬組的Group ID

  • 檔案的讀、寫、執行許可權

  • 檔案的時間戳

  • 連結數,即有多少檔名指向這個inode 檔案資料block的位置

    可以通過stat 指令檢視對應檔案的inode資訊,如下圖所示:

image-20210822161934940

inode中還會為該檔案維護一個索引表,類似於記憶體管理中的頁表記錄了邏輯塊號到物理塊號之間的對應關係。索引表的目錄項中記錄的是每個檔案的索引塊地址。一般有直接索引,多層索引或混合索引等。

image-20210822164636231

inode點陣圖:一個檔案系統能分配的inode數量是一定的,因此也可以用記錄block的方式來記錄inode的分配情況。原理與block Bitmap的原理一樣,inode Bitmap中的“下標”就是inode號。當分配了某個inode號時,將對應inode Bitmap位元位置為1。

2.3 目錄檔案的理解

在Linux中,目錄(directory)也是一種檔案。

目錄檔案是一系列目錄項(dirent)組成的。每個目錄項,由兩部分組成:所包含檔案的檔名,以及該檔名對應的inode號碼。如下圖所示

image-20210822170449438

既然目錄也是檔案,那麼目錄本身也一定有其inode,現代作業系統一般採用樹形結構來組織檔案。因此要開啟一個檔案,需要先找到該檔案的目錄,並在目錄中找到對應的目錄項,獲取到該檔案的inode號碼,再將磁碟中的內容載入到記憶體。

進入一個目錄需要什麼許可權?

顯示目錄下的內容是讀許可權(r) ,由於目錄檔案內只有檔名和inode號碼,所以如果只有讀許可權,只能獲取檔名,無法獲取其他資訊,因為其他資訊都儲存在inode節點中。進入是可執行許可權 (x),一個目錄預設需要有可執行許可權。

2.4 軟連結與硬連結

我們已經知道,檔案的唯一識別符號是inode而非檔名,檔名僅僅是方便使用者使用的一個“綽號”。那麼,只能通過一個檔名來找到對應的inode,獲取檔案的元資訊嗎?在Linux中,為了解決檔案共享的問題,提出了連結。連結分為硬連結和軟連結:

2.4.1 硬連結

讓不同的檔名訪問同樣的內容,這種方式被稱為硬連結。

可以使用ln指令建立硬連結:ln 原始檔 目標檔案,如下圖所示

image-20210822180347412

該案例中為test可執行檔案建立了一個硬連結linktest,通過ls -il指令可以看到,linktest和test具有相同的inode號碼,也就是說實際上是同一個檔案,此時連結數為2。執行test和linktest後,都會輸出同樣的結果。而當刪除test檔案後,由於linktest的存在,該檔案實際上並未被刪除,刪除的僅僅是該檔名!此時,連結數會變為1。

因此我們可以總結出硬連結的如下特點:

  • 不同的檔名訪問同樣的內容。
  • 對檔案內容進行修改,會影響到所有檔名。
  • 刪除一個檔名,不影響另一個檔名的訪問。當連結數變為0時,該檔案才算真正刪除。

這裡需要注意目錄檔案的連結數!

建立一個目錄檔案,輸入ls -il命令後,會看到如下現象:

image-20210822185613313

該目錄檔案的連結數居然是2!這是為什麼?當我們進入dir,並建立一個目錄檔案後,會發現,“."的inode和dir的inode是一樣的,也就是說dir目錄下的“."是dir的硬連結。這是因為建立目錄時,預設會生成兩個目錄項,".“和"..",”." 代表當前檔案,".." 代表上一級檔案。

image-20210822185838208

如果此時我們在dir下再建立一個目錄,會發現dir的連結數變為3。這是因為新建立的檔案dir/dir1中包含”.." ,該檔名也是dir的硬連結。

image-20210822190723350

綜上,任何一個目錄的"硬連結"總數,總是等於2加上它的子目錄總數(含隱藏目錄)

2.4.2 軟連結

軟連結類似於Windows下的快捷方式。Linux下通常會將一些目錄層次較深的檔案連結到一個更易訪問的目錄中。

用ln -s 命令建立一個檔案的軟連結:ln -s 源文檔案或目錄 目標檔案或目錄

image-20210822212327508

可以看到建立test的軟連結slinktest後,slinktest的inode號與test的inode號不一樣。這說明軟連結本身也是一個檔案,且檔案型別為l。test的連結數始終為1,當刪除test後,執行slinktest會報出No such file or directory的錯誤。也就是說,軟連結依賴於原檔案存在。

硬連結和軟連結最大的不同在於:

  • 軟連結指向的是原檔案的檔名而不是inode,儲存了其代表的檔案的絕對路徑,不會改變原檔案的連結數,刪除原檔案,軟連結將不能使用。
  • 而硬連結指向的原檔案的inode,只有當連結數變為0是,整個檔案才能被真正刪除。

3.動靜態庫

在學習動靜態庫前,我們需要先明白什麼是庫。庫(Library)就是一段編譯好的二進位制程式碼,加上標頭檔案後就可以供別人使用。一般有兩種情況會用到庫:

  • 第一種情況是某些程式碼需要給別人使用,但是我們不希望別人看到原始碼,就編譯好並以庫的形式進行封裝,只暴露出標頭檔案。別人要使用,只需要加上標頭檔案即可。
  • 第二種是在實際工程中,編譯一個大型專案往往要花費很多時間,因為很多檔案都需要從原始檔編譯,連結。對於某些不會進行大的改動的程式碼,我們想減少編譯的時間,就可以把它打包成庫,因為庫是已經編譯好的二進位制了,編譯的時候只需要 Link 一下,不會浪費編譯時間。

那麼,我們必須明白一個概念——目標檔案。目標檔案有三種形式:

  • 可執行目標檔案。即我們通常所認識的,可直接執行的二進位制檔案。
  • 可重定位目標檔案。包含了二進位制的程式碼和資料,可以與其他可重定位目標檔案連結,並建立一個可執行目標檔案。
  • 共享目標檔案。它是一種在載入或者執行時進行連結的特殊可重定位目標檔案。當程式執行到一定程度,需要呼叫該目標檔案中的某個介面時,才會將該目標檔案與執行中的檔案連結。

通過上面的表述,我們發現連結的方式有兩種:一種是提前連結好,生成可執行目標檔案;另一種是執行過程中才連結。前者被稱為靜態連結,後者被稱為動態連結。於是便產生了兩種庫——靜態庫與動態庫。

靜態庫在Linux中字首為lib,字尾為.a,因此一個靜態庫的名字為libxxx.a。動態庫在Linux中字首為lib,字尾為.so,則一個動態庫的名字為libxxx.so。

靜態庫(.a):程式在編譯連結的時候把庫的程式碼連結到可執行檔案中,程式執行的時候將不再需要靜態庫 。

image-20210822220515361

當我們make後,會發現報如下錯誤:

/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
make: *** [test_static] Error 1

不要著急,這是因為我們沒有安裝靜態庫!

輸入如下指令:sudo yum install glibc-static即可解決。接下來編譯,會發現出現了我們想要的test_static。發現沒有,通過靜態庫生成的目標檔案非常大!還請大家忍一下。

image-20210822221116005

這是因為通過靜態庫生成的可執行檔案時,在連結的過程中將靜態庫中需要的部分都“拷貝”到了最終的可執行檔案中,因此這個可執行檔案在一個沒有其需要的庫的linux系統中也能正常執行。

動態庫(.so):程式在執行的時候才去連結動態庫的程式碼,多個程式共享使用庫的程式碼。 一般預設生成的可執行程式都是動態的,動態庫體積小,執行時載入,只有一份。可以看到,動態連結生成的test的大小隻有靜態連結生成的test_static的百分之一左右!

image-20210822222452273
  • 一個與動態庫連結的可執行檔案僅僅包含它用到的函式入口地址的一個表,而不是外部函式所在目標檔案的整個機器碼。
  • 在可執行檔案開始執行以前,外部函式的機器碼由作業系統從磁碟上的該動態庫中複製到記憶體中,這個過程稱為動態連結(dynamic linking)。
  • 動態庫可以在多個程式間共享,所以動態連結使得可執行檔案更小,節省了磁碟空間。作業系統採用虛擬記憶體機制允許實體記憶體中的一份動態庫被要用到該庫的所有程式共用,節省了記憶體和磁碟空間。

可以通過file命令檢視檔案的連結資訊:

image-20210822223003765

通過以上描述,我們可以看出動態庫與靜態庫有以下區別:

  1. 可執行檔案大小不同。動態庫比靜態庫小得多。
  2. 擴充套件性不同。如果靜態庫中某個函式的實現變了,那麼這個可執行檔案必須重新編譯,比較耗時。而動態庫只需要更新動態庫本身,不需要重新編譯可執行檔案。
  3. 載入速度不同。由於靜態庫在執行時才連結,因此從時間效率上會稍慢一些。不過由於程式執行的區域性性原理,時間損失並不會很多。
  4. 依賴性不同。靜態連結的可執行檔案不需要依賴其他的內容即可執行,而動態連結的可執行檔案必須依賴動態庫的存在。一般情況下,系統中有大量的動態庫,不會有太大問題。

總結

學習完系統IO後,我們再來思考最後一個問題。

當檔案開啟載入進記憶體後,該檔案在記憶體中的位置為什麼不放在inode中,而是存放在file結構體中?

Linux中的檔案是能夠共享的,假如把檔案位置存放在索引節點中,則如果有兩個或更多個程式同時開啟同一個檔案時,它們將去訪問同一個索引節點。如果一個程式對該檔案進行寫操作,而另一個同時進行讀操作,顯然,這是不被允許的。

另一方面,開啟檔案時有如下特點。

  • 一個檔案不僅可以被不同的程式分別開啟,而且也可以被同一個程式先後多次開啟。
  • 一個程式如果先後多次開啟同一個檔案,則每一次開啟都要分配一個新的檔案描述符,並且指向一個新的file結構。

引入file結構體有利於檔案的共享。當兩個程式共享同一個檔案時,兩個程式的fd可以指向同一個file結構體。file結構體中記錄著檔案的在記憶體中的偏移量,當一個程式進行寫操作後,檔案的偏移量可能發生改變,此時只需要修改file結構體中的偏移量。當該程式寫結束,另一個程式需要進行寫操作時,是在新的偏移量的基礎上進行寫操作,這樣防止了第二個程式重寫第一個程式的輸出內容。

程式可以共享同一個開啟的檔案,那程式之間是否能夠進行通訊呢?答案是肯定的。下一章我們將會講述《Linux系統程式設計之程式通訊》,如果覺得有用,歡迎您一鍵三連!

相關文章