文章目錄
- 前言
- 基礎IO定義
- 系統IO接口
- 文件描述符
- 重定向原理
- 緩沖區刷新
前言
要知道每個函數/接口的全部參數和返回值建議去官網或者直接在Linux的man手冊中查,這不是復制粘貼函數用法的文章。
C語言文件讀寫介紹鏈接
基礎IO定義
IO是Input/Output的縮寫,它是計算機領域中常用的術語,用來描述計算機系統與外部設備之間的數據交換過程。輸入(Input)是指將外部數據或指令傳輸到計算機系統中,而輸出(Output)則是指將計算機系統處理后的數據或結果傳輸到外部設備中。例如,鍵盤、鼠標、顯示器、打印機等都屬于外部設備,它們與計算機之間的數據交換過程就是通過輸入和輸出來實現的。
系統IO接口
在說系統IO接口之前需要區分語言庫IO函數和系統IO接口的區別,庫函數IO接口如C語言中的fopen函數 fseek函數 ftell函數 rewind函數
等。這些都是語言庫對系統IO接口open write read close
等IO接口的再封裝。 如圖open write read close
等IO接口在用戶操作接口層,fopen函數 fseek函數 ftell函數 rewind函數
等在用戶層。
文件描述符
文件描述簡寫為fd;
在Linux中,每個進程都有一個task_struct,
task_struct 里有 *files指針
, *files指針指向 files_struct結構體
(files_struct結構體內含有file_struct
結構體的列表的指針) , fd
是 files_struct 內那個指向的數組 的下標,文件描述符本質是文件信息結構體數組下標。(注意files_struct
和 file_struct
差一個字母 )
在調用系統IO接口open打開文件后會返回打開文件描述符。文件描述符是一個非負的整數。在Linux操作系統中的進程中,默認會打開三個文件描述符,分別是0 , 1 , 2
對應三個文件標準輸入文件
標準輸出文件
標準錯誤文件
。(Linux中一切皆文件,硬件如:顯示器,鍵盤鼠標接入后都是Linux系統中的一個個文件)
文件描述符的分配原則是,當一個進程打開新的文件,該文件的信息存放在文件信息存儲數組中未被使用的且素組下標最小的位置。(也就是說如果默認被標準輸入文件使用的0下標在新文件被打開之前就關閉,新文件打開后就會占據0下標來記錄新打開文件的文件信息)
重定向原理
重定向原理:關閉文件信息數組newfd下標對應的文件,并將newfd存儲的文件設置為oldfd存儲的文件信息。此時newfd 和 oldfd 文件描述符實際對應的都是重定向之前 newfd對應的文件信息,即可通過newfd 和 oldfd 文件描述訪問同一個文件。
將oldfd實際對應的文件信息給newfd
重定向使用
使用方法一:利用函數
int dup2(int oldfd, int newfd);
參數:
1.oldfd:一個整數值,表示要復制的舊文件描述符。
2.newfd:一個整數值,表示新的文件描述符。
返回值:
如果成功,返回值為newfd;
如果失敗,返回值為-1,并設置errno來指示錯誤類型。
例:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>int main()
{// 打開一個文件用于寫入int fd = open("example.txt", O_WRONLY | O_CREAT, 0644); if (fd == -1) {perror("open");return 1;}// 將標準輸出重定向到文件描述符fd所指向的文件int newfd = dup2(fd, STDOUT_FILENO); if (newfd == -1) {perror("dup2");return 1;}printf("This will be written to example.txt\n"); // 這行內容將會寫入到example.txt文件中close(fd); // 關閉文件描述符fdclose(newfd); // 關閉新的文件描述符newfdreturn 0;
}
使用方法二:在命令行利用重定向符號 < >
在Linux中,重定向符號>用于將命令的輸出結果重定向到指定的文件中。它的作用是將命令的標準輸出(stdout)輸出到文件中,而不是顯示在終端上。
重定向符號>的使用方法是在命令后面加上>符號,緊跟著要輸出到的目標文件名。例如:
command > filename
command表示要執行的命令,filename表示要將輸出結果寫入的目標文件名。
當執行帶有重定向符號>
的命令時,如果目標文件已經存在,則會被覆蓋;如果目標文件不存在,則會創建一個新文件。重定向符號>會將命令的標準輸出重定向到目標文件中,不會在終端上顯示輸出結果。以下是幾個示例,演示如何使用重定向符號>進行輸出重定向:
- 將ls命令的輸出結果寫入到名為file.txt的文件中:
ls > file.txt
- 將command命令的錯誤輸出(標準錯誤流)寫入到名為error.txt的文件中
command 2> error.txt
緩沖區刷新
什么是緩沖區
緩沖區區分
前面IO分為系統IO和語言庫封裝的IO函數,語言庫在封裝IO接口的同時也對IO緩沖區刷新策略做了封裝,C語言的緩沖刷新策略和Linux本身緩沖區刷新策略大致一樣。但C語言的函數在系統緩沖區的基礎上,在語言庫層面(用戶層)再設置了一個緩沖區,該緩沖區具體在FILE結構體中。
Linux 緩沖區刷新策略:
-
全緩沖(fully buffered):默認情況下,Linux使用全緩沖模式。在全緩沖模式下,數據會在緩沖區中累積一定量后才會被寫入磁盤,這樣可以減少磁盤I/O操作的次數,提高性能。但是,這也意味著數據可能會在緩沖區中停留一段時間,直到緩沖區滿或者手動刷新。
-
行緩沖(line buffered):對于某些特殊的文件,如終端設備,Linux會使用行緩沖模式。在行緩沖模式下,數據會在遇到換行符時立即寫入磁盤,這樣可以保證及時顯示輸出結果。但是,對于普通文件,行緩沖模式并不常見。
-
無緩沖(unbuffered):在某些情況下,我們可能需要禁用緩沖區,直接將數據寫入磁盤。這種模式下,數據會立即寫入磁盤,但是由于沒有緩沖區,會導致頻繁的磁盤I/O操作,性能較差。
庫函數刷新策略證明
#include <stdio.h>
#include <string.h>
int main()
{const char *msg0="hello printf\n";const char *msg1="hello fwrite\n";const char *msg2="hello write\n";printf("%s", msg0);fwrite(msg1, strlen(msg0), 1, stdout);write(1, msg2, strlen(msg2));fork();return 0;
}
直接運行結果:
hello printf
hello fwrite
hello write
./test > file 重定向之后結果:
hello write
hello printf
hello fwrite
hello printf
hello fwrite
現象解釋:C語言庫函數緩沖區在輸入對象為終端時刷新策略為行緩沖,直接運行時,在遇到換行符時就將庫函數緩沖區的內容刷新到系統緩沖區,再由系統緩沖區輸入到終端上。重定向后,輸入目標為普通文件,刷新策略變為全緩沖,write輸入到系統緩沖區,fwrite 和 printf 屬于C語言庫函數,輸入了C語言庫函數緩沖區,在子進程創建后,會拷貝一份C語言庫函數緩沖區 (因為拷貝了FILE結構體,C語言庫函數緩沖區即為FILE結構體的成員) 到子進程。 進程結束時會將父子進程C語言庫函數的緩沖區的內容輸入系統緩沖區,再由系統緩沖區一起刷新到終端(屏幕)。