Linux下檔案輸入輸出操作

2020-08-09 14:26:07

標準庫的IO介面

檔案的開啓方式

r:只讀
r+:讀寫
w:只寫
w+:讀寫
a:追加寫(每次寫入數據總是寫入到檔案末尾)
a+:追加讀寫
b:二進制操作

  • r+w+ 的區別:r+讀寫開啓檔案時若檔案不存在則報錯;w+讀寫開啓檔案時若不存在則建立,若存在則清空原有內容
  • a不僅僅是追加寫,並且檔案不存在還會建立新檔案
  • 如果不指定b則預設檔案是文字操作,加上b則認爲是二進制操作;區別在於有時一個特殊字元佔據了兩個位元組的記憶體(讀取一個100位元組大小的檔案,文字操作最終讀取出來的數據可能不到100個位元組)

IO介面

fopen

FILE* fopen(char* filename,char* mode);		//filename--檔名稱,mode--開啓方式
  • 返回值:返回一個FILE*的檔案流指針作爲檔案的操作控制代碼;失敗則返回NULL
  • 開啓檔案之後 一定不能忘了close關閉檔案

fread、fwrite

// buf--緩衝區,block_size--塊大小,block_count--塊個數,fp--檔案流指針
size_t fread(char* buf,size_t block_size,size_t block_count,FILE* fp);
// data--數據首地址,block_size--塊大小,block_count--塊個數,fp--檔案流指針
size_t fwrite(char* data,size_t block_size,size_t block_count,FILE* fp);
  • fread和fwrite操作的數據實際大小是塊大小*塊個數
  • 返回值:返回實際操作的塊個數
  • 例:讀取一個檔案size=10,count=2;若檔案大小足夠則返回2,如檔案大小隻有16位元組則返回1,因爲第二塊沒讀滿
  • fread如果讀到了檔案末尾會返回0;
  • 若讀取1000個位元組,塊個數爲1,檔案大小隻有512位元組,雖然讀取了512位元組的數據但也會返回0
  • fread/fwrite比較推薦塊大小爲1,因爲塊大小爲1時塊個數就是要操作的數據長度

fseek

int fseek(FILE* fp,long offset,int whence);
  • 將檔案的讀寫指針從whence位置偏移offset個位元組–跳轉檔案讀寫位置
  • 可偏移負數個位元組(向前偏移),檔案沒有數據也可以跳轉讀寫位置

fclose

int fclose(FILE* fp);
  • 關閉檔案流指針,釋放資源

Linux下系統呼叫IO介面

open
開啓指定檔案

int open(char* filename,int flag,mode_t mode);
  • filename:要開啓的檔名稱
  • flag:選項參數----檔案的開啓方式(必選項/可選項)
    • 必選項(只能選其一):O_RDONLY:只讀;O_WRONLY:只寫;O_RDWR:可讀可寫
    • 可選項:O_CREAT:檔案存在則開啓不存在則建立;O_EXCL與O_CREAT同時使用:檔案存在則報錯;O_TRUNC:開啓檔案的同時清空原有內容;O_APPEND:追加寫(寫入到末尾)
  • mode:許可權;如果使用了O_CREAT有可能建立新檔案,就一定要指定檔案許可權(八進制數位形式);若要操作檔案,最好程式初始化時呼叫umask(0);將當前進程的umask值設定爲0,這樣在設定許可權時就不需要再進行計算
  • 返回值:一個非負整數----檔案描述符(檔案在程式碼中的操作控制代碼),失敗返回-1

write
向指定檔案中寫入指定長度的數據

ssize_t write(int fd,char* data,size_t len);
  • fd:open返回的檔案描述符(檔案操作控制代碼),通過這個fd指定要往哪個檔案寫入數據
  • data:要寫入檔案的數據的空間首地址
  • len:要寫入的數據大小
  • 返回值:返回實際寫入檔案的數據位元組長度;失敗返回-1

read
從檔案中讀取指定長度的數據,放到buf中

ssize_t read(int fd,char* buf,size_t len);
  • fd:open返回的檔案描述符
  • buf:從檔案中讀取數據放到哪塊緩衝區中的首地址
  • len:想要讀取的數據長度,這個len不能大於緩衝區大小
  • 返回值:返回實際讀取到的數據位元組長度;失敗返回-1

lseek
跳轉讀寫位置

off_t lseek(int fd,off_t offset,int whence);
/*例*/	lseek(fd,0,SEEK_END);	//返回的剛好爲檔案大小
  • fd:open返回的檔案描述符
  • offset:偏移量
  • whence:從哪裏開始偏移。SEEK_SET:從檔案起始位置,SEEK_CUR:從檔案當前讀寫位置,SEEK_END:檔案末尾
  • 返回值:成功返回當前位置相對於起始位置的偏移量;失敗返回-1

close
通過檔案描述符關閉檔案,釋放資源

int close(int fd);
  • fd:檔案描述符

檔案描述符

概念:檔案描述符其實是內核中一個檔案描述資訊陣列的下標,通過這個下標可以在內核中找到相應的檔案描述資訊,通過描述資訊可以實現檔案操作

  • 檔案描述符(操作檔案的控制代碼)是一個非負整數
  • 檔案描述符的分配規則:最小未使用
  • 一個進程中預設會開啓三個檔案:標準輸入:0,標準輸出:1,標準錯誤:2
  • 爲什麼一個檔案在不操作後一定要關閉?
    釋放資源,檔案描述符是有限的。若不關閉檔案,描述符用光,進程中就無法開啓新檔案了

檔案描述符與檔案流指針的關係

  • 檔案描述符:檔案描述資訊陣列的下標,是一個非負整數,是系統呼叫的IO介面
  • 檔案流指針FILE是一個結構體(typedef struct _IO_FILE FILE),有一個成員就是檔案描述符,是庫函數IO介面的操作控制代碼
  • 我們通常說的緩衝區實際上是檔案流指針結構體中的緩衝區,這個緩衝區通常被稱之爲使用者態緩衝區

緩衝區

  • 向檔案寫入數據,並不會直接寫入檔案,而是寫入緩衝區,重新整理緩衝區的時候纔會寫入檔案
  • 系統呼叫介面是直接將數據寫入檔案的,系統呼叫介面沒有緩衝區,只有庫函數纔有緩衝區
  • exit退出會重新整理緩衝區/_exit退出時不會重新整理緩衝區

重定向

改變描述符所指向的檔案描述資訊,改變當前描述符所操作的檔案,最終改變數據的流向。實際是描述符的重定向,將數據不再寫入原本的檔案,而是寫入新的指定檔案中

int dup2(int oldfd,int newfd)	//描述符重定向函數
  • 讓newfd這個描述符也指向oldfd所指向的檔案,這時候oldfd和newfd都能操作oldfd所指向的檔案

檔案系統

檔案系統就是磁碟上管理檔案的系統。linux下ext2檔案系統將磁碟分爲五個區域:超級塊inode bitmap1data bitmapinodedata

檔案的儲存:通過超級塊找到inode點陣圖/數據塊點陣圖,通過數據塊點陣圖快速找到空閒的磁碟塊儲存檔案數據,通過inode點陣圖快速找到空閒的inode節點,儲存檔案的元資訊

  • 目錄項:檔名+inod節點號。Linux下檔案的數據和檔案的名稱是分離的,檔案的數據以及inode資訊儲存完畢之後,將目錄項儲存到父目錄檔案中

檔案的獲取:通過檔名到父目錄檔案中找到檔案對應的目錄項,得到檔案的inode節點號,在磁碟超級塊中找到inode節點區域,根據inode節點號,快速找到inode節點,得到數據儲存的磁碟塊號(數據塊位置),進而獲取到檔案數據

軟鏈接檔案、硬鏈接檔案

軟鏈接檔案/硬鏈接檔案:給一個原始檔建立一個軟鏈接檔案/硬鏈接檔案,就可以通過被建立出來的軟鏈接/硬鏈接檔案來操作原始檔

ln test.txt test.hard		// 爲原始檔建立一個硬鏈接檔案
ln -s test.txt test.soft	// 爲原始檔建立一個軟鏈接檔案

軟連線檔案和硬鏈接檔案的區別:

軟連線檔案 硬鏈接檔案
軟鏈接檔案是獨立的檔案,有自己的inode號,裏面儲存着原始檔的路徑,通過路徑存取原始檔數據 硬鏈接檔案是一個檔案的目錄項(只是原始檔的別名),和原始檔共用一個inode號,通過inode節點存取原始檔數據
刪除原始檔,軟鏈接檔案失效 刪除原始檔, 硬鏈接檔案只是鏈接數-12 3,依然可以存取原始檔數據
軟鏈接檔案可以對目錄建立 硬鏈接檔案不可以對目錄建立
軟鏈接檔案可以跨分割區建立 硬鏈接檔案不可以跨分割區建立

庫的打包與使用

庫檔案:打包了一堆實現了常用功能的程式碼檔案

打包流程

  1. 將各個高階語言程式碼(.c檔案等)編譯彙編成爲機器指令。例:gcc -c child.c -o child.o;生成動態庫時最好加上-fPIC產生位置無關的程式碼:gcc -c -fPIC child.c -o child.o
  2. 將所有的.o檔案以及庫檔案打包生成自己的庫檔案
    動態庫的打包:gcc -shared child.o -o libmychild.so;動態庫的命名方式:lib***.so
    靜態庫的打包:ar -cr libmychild.a child.o;靜態庫命名方式:lib***.a

庫的使用

生成可執行程式時鏈接使用

  • 將庫檔案放到指定路徑下:/usr/lib64或/usr/lib
  • 設定鏈接庫的搜尋路徑環境變數,將當前庫檔案所在路徑新增進去:export LIBRARY_PATH=$LIBRARY_PATH:.
  • 使用gcc -L選項指定鏈接庫的搜尋路徑:gcc main.c -o main -L. -lmychild

執行可執行程式時載入使用:僅僅針對動態庫(只有動態庫纔會在執行時載入庫檔案)

  • 必須將庫檔案放到指定路徑下:/usr/lib64或/usr/lib
  • 設定鏈接庫的載入路徑環境變數,將當前庫檔案所在路徑新增進去:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

  1. 點陣圖:一連串的二進制位元位,用來做一種數據標記 ↩︎

  2. 鏈接數:一個inode節點對應有幾個目錄項 ↩︎

  3. 刪除一個檔案,檔案並不會立即被刪除,而是刪除了目錄項資訊,inode中的鏈接數-1,只有鏈接數爲0時,纔會真正刪除檔案 ↩︎