Linux文件:重定向底層實現原理(輸入重定向、輸出重定向、追加重定向)
- 前言
- 一、文件描述符fd的分配規則
- 二、輸出重定向(>)
- 三、輸出重定向底層實現原理
- 四、追加重定向(>>)
- 五、輸入重定向(<)
- 六、系統調用dup2
- 七、標準錯誤stderr存在意義
前言
?在Linux中,操作系統會為每一個文件創建對應的描述結構體對象struct file
。該結構體中一定存在3個部分:打開文件的所有屬性、文件的操作集、文件緩沖區(內存)。其中由于馮諾依曼體系決定了,無論對文件進行讀操作還是寫操作,都需要先將數據加載到文件緩存區!
?我們在應用層進行對數據讀寫的操作本質上是用戶緩沖區和內核數據緩沖區之間的相互拷貝!!!
一、文件描述符fd的分配規則
- 默認情況下,進程會默認打開3個文件:標準輸入、標準輸出、標準錯誤。
- 文件描述符的分配規制是:從上往下遍歷查找最小的、未被使用的分別個新的文件!
二、輸出重定向(>)
?下面我們將標準輸出文件關閉,然后創建新的文件;并向新的文件中輸出一些信息。
int main()
{close(1); //將標準輸出文件顯示器關閉int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);//新創建文件,fd分配為1 if(fd < 0) { perror("open"); return 1; } printf("what will happen\n"); return 0;
}
- 我們將
stdout
關閉后,根據文件描述符的分配規則,新創建的文件fd為1。 - 我們觀察結果發現,原本應該向顯示器打印的消息直接向新創建的文件中寫入。我們將這種現象稱為輸出重定向
>
!!
三、輸出重定向底層實現原理
?在C中,printf
函數只能向標準輸出文件中輸出消息,更準確的說:printf
只認文件描述符為1對應的文件。
?當我們關閉標準輸出后,新創建的文件所分別的文件描述符fd為1。此時文件描述符表中,下標為1的數組內容由標準輸出替換為新打開的文件log.txt
。此時我們調用printf
輸出消息時,變為向log.txt
文件中寫入!!
所有重定向的本質就是修改特定文件fd的下標內容!!上層fd不變,下層fd指向的內容發生改變!
四、追加重定向(>>)
?追加重定向和輸出重定向基本相同,主要在于新創建文件的打開方式從w
改為a
。在底層上和輸出重定向一樣,都是將fd為1的下標內容替換為新文件即可!!!
int main()
{close(1); //將標準輸出文件顯示器關閉int fd = open("log.txt", O_CREAT | O_WRONLY | O_APPEND, 0666);//新創建文件,fd分配為1 if(fd < 0) { perror("open"); return 1; } printf("what will happen\n"); return 0;
}
五、輸入重定向(<)
?輸入重定向<
本質就是將待重定的文件內容替換到fd為0的下標位置。此時我們熟悉的scanf、fputs
等函數讀取方式會從鍵盤轉變為新文件。即直接讀取新文件中的內容
int main()
{close(0); //將顯示器關閉 int fd = open("log.txt", O_RDONLY); //新創建文件fd為0if(fd < 0) { perror("open"); return 1; } char buffer[1024]; fread(buffer, 1, 1024, stdin);//從文件log.txt中讀寫信息 printf("stdin->fd:%d, %s\n", stdin->_fileno, buffer); return 0;
}
- 在上述代碼執行過程中,沒有出現光標等待我們從鍵盤上輸入信息,而是直接將
log.txt
中的信息輸出,進一步說明了fd為0
的下標內容被替換。
六、系統調用dup2
?上述進行重定向時時進行的fd指向的內容替換,都需要我們手動先將對應的1、2、3
號文件關閉,非常麻煩!為此,系統中提供了相關的系統調用接口:dup2
。
#include <unistd.h>
int dup2(int oldfd, int newfd);//These system calls create a copy of the file descriptor oldfd
【實例】:
int main()
{int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);dup2(fd, 1);printf("this is log.txt test\n"); return 0;
}
七、標準錯誤stderr存在意義
?標準輸出和標準錯誤都是向顯示器上打印信息。既然都是向同一個硬件進行寫入,為什么還需要標準錯誤的存在?
?在項目實際運行過程中,bug是不可避免的。所以在日志中會記錄很多正確信息和錯誤信息。而stderr存在的意義是將兩者信息分開,將所有的錯誤信息全部寫入標準錯誤文件中,從而降低排除的成本!!
【實例】:
- 下面我們通過輸出
hello stdin
和hello Stderr
來模擬日志中的正確信息和錯誤錯誤信息!
int main()
{fprintf(stdout, "hello stdin\n"); fprintf(stderr, "hello Stderr\n");}
我們情況下,兩者都在顯示器上直接輸出
我們也可以通過替換fd=1
和fd=2
的下標對應文件,來將兩者信息分開,分別向兩個文件中寫入
- 我們也可以將兩種信息全部打印到同一個文件中:
1>log.txt 2>&1
。 這段代碼的意思是先用log.txt文件fd中的內容替換fd=1
下標中的內容,然后&1
獲得下標fd=1
中保存的數據(即指向log.txt
),最后將fd=2
對應的內容用fd=1
中的內容替換!!即fd=1
和fd=2
都指向了log.txt
文件。 - 前面講述的輸入重定向、輸出重定向、追加重定向都是簡寫的,原始版本應該和上面重定向一樣,加上對應的fd。例如輸出重定向
./myfile > log.txt
是./myfile 1 > log.txt
簡寫。