文章目錄
- 一、再來理解重定向
- 1.1 輸出重定向效果演示
- 1.2 重定向的原理
- 1.3 dup2
- 1.4 輸入重定向效果演示
- 1.5 輸入重定向代碼實現
- 二、再來理解標準輸出和標準錯誤
- 2.1 同時對標準輸出和標準錯誤進行重定向
- 2.2 將標準輸出和標準錯誤重定向到同一個文件
- 三、再看一切皆文件
- 四、結語
一、再來理解重定向
1.1 輸出重定向效果演示
分析:ls
指令是顯示當前目錄下的文件,本質就是將當前目錄下所有的文件名以字符串的形式寫入到顯示器文件。采用輸出重定向 >
,將原本應該寫入顯示器文件的內容寫入到了 log.txtx
文件中。
1.2 重定向的原理
在講解重定向原理前,我們需要明確文件描述符的分配規則,即從0下標開始,尋找最小的沒有使用的數組位置,它的下標就是新打開文件的文件描述符。這里沒有使用的意思是該下標里面存的是 NULL
,即沒有指向任何一個文件對象。下面通過一段代碼來為大家展示重定向的原理。
// mytest.c
int main()
{close(1);int fd = open(FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0666);if(fd < 0){perror("open");return errno;}const char* str = "Hello Linux!\n";int cnt = 5;while(cnt--){write(1, str, strlen(str));}return 0;
}
代碼分析:上面這段代碼就完美的展示了重定向的原理。首先調用 close
系統調用將 1 號下標對應的文件關閉,關閉的意思就是將 1 下標里的內容置為 NULL
,原本 1 下標里面存儲的內容是顯示器文件對象的地址,也就是標準輸出 stdout
,緊接著調用 open
打開了一個文件,根據文件描述符的分配規則,新打開的這個文件的文件描述符就是 1,即文件描述符表(file*
的數組)1 號下標里面存儲的就是新打開的文件對象的地址。接下來調用 write
接口,向 1 號文件描述符中進行寫入,本來 1 號文件描述符對應的是顯示器文件,原本向顯示器文件中寫入的內容,此時就被寫入到新打開的文件中,沒有向顯示器文件中寫入,因此屏幕上就不會出現字符串,至此整個重定向的過程就結束啦。
總結:重定向的本質是對數組下標里面的內容進行修改。
1.3 dup2
上面介紹了重定向的原理,下面介紹一下實現重定向的系統調用 dup2
。
#include <unistd.h>
int dup2(int oldfd, int newfd);
dup2
的具體實現并不是向上面代碼中那樣,先將一個文件描述符關閉,然后緊接著再打開一個文件。dup2
的使用方法是,用戶在調用 dup2
接口前,正常打開一個文件,不用將顯示器文件關閉,此時新打開文件的文件描述符就是 3。接下來調用 dup2
,將新打開文件的文件描述符作為 oldfd
,將顯示器文件的文件描述符也就是 1,作為 newfd
。我們知道,文件描述符本質上就是數組下標,dup2
函數中執行的工作就是將 oldfd
下標里存儲的文件對象地址拷貝到 newfd
下標里面,至此重定向工作就完成了。
小Tips:dup2
的函數形參有一個誤導,我們可能會覺得新打開文件的描述符是 newfd
,其實不然,這里的 newfd
是將要被覆蓋的文件描述符,oldfd
是新打開文件的描述符。
int main()
{// close(1);int fd = open(FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0666);if(fd < 0){perror("open");return errno;}dup2(fd, 1);const char* str = "Hello Linux!\n";int cnt = 5;while(cnt--){write(1, str, strlen(str));}return 0;
}
代碼分析:上面就是輸出重定向的實現原理,追加重定向只需要把 O_TRUNC
替換成 O_APPEND
。
1.4 輸入重定向效果演示
分析:cat
指令本來是從鍵盤文件中獲取輸入然后寫入顯示器文件中,采用輸入重定向 <
后,是從 log.txt
文件中獲取輸入然后寫入顯示器文件中。
1.5 輸入重定向代碼實現
// 輸入重定向
int main()
{int fd = open(FILE_PATH, O_RDONLY);if(fd < 0){perror("open");}dup2(fd, 0);char str[1024];ssize_t ret = read(fd, str, sizeof(str) - 1);if(ret > 0){str[ret] = '\0';printf("echo: %s", str);}return 0;
}
小Tips:進程歷史打開的文件與進行的各種重定向關系都和未來進行的程序替換無關,程序替換并不影響文件訪問。進程打開文件和何種重定向工作,本質上都是進程管理的模塊,而程序替換只會把用戶空間的代碼和數據完全被新程序替換,不會影響到進程管理。
二、再來理解標準輸出和標準錯誤
int main()
{fprintf(stdout, "Standard output messages\n");fprintf(stdout, "Standard output messages\n");fprintf(stdout, "Standard output messages\n");fprintf(stderr, "Standard error messages\n");fprintf(stderr, "Standard error messages\n");fprintf(stderr, "Standard error messages\n");return 0;
}
代碼分析:>
是輸出重定向,也就是對標準輸出(1號文件描述符)進行重定向。標準錯誤對應的2號文件描述符并沒有進行重定向,因此標準錯誤消息仍然打印在了屏幕上。
2.1 同時對標準輸出和標準錯誤進行重定向
./mytest 1>output.txt 2>error.txt
小Tips:這段代碼就是將1號文件描述符對應的標準輸出文件重定向到 output.txt 文件,將2號文件描述符對應的標準錯誤文件重定向到 error.txt 文件。這樣以來屏幕上就不會有任何輸出。
2.2 將標準輸出和標準錯誤重定向到同一個文件
./mytest 1>all.txt 2>&1
小Tips:將標準輸出和標準錯誤都重定向到 all.txt 文件中。
三、再看一切皆文件
所有操作計算機的動作,都是通過進程去執行的,所有的訪問文件操作,都是通過進程去實現的,目前所有對文件的操作都依賴于進程。
小Tips:所有的外設都被抽象成了文件,每個外設都有自己的讀寫方法,不同的外設讀寫方法一定是不同的。但是我們在對文件進行讀寫操作的時候,始終調用的都是 read
和 write
方法,這是因為操作系統為我們提供了一個方法集類型 file_operations
,該結構體里面都是函數指針類型,指向外設的各種方法,這就是多態的雛形。所謂的一切皆文件,就是操作系統幫我們封裝了一層文件對象,進程對各種外設的操作,全都變成了對文件的操作。
sszie_t read(int fd)
{task_struct->files->fd_array[fd]->f_op->read();
}
四、結語
今天的分享到這里就結束啦!如果覺得文章還不錯的話,可以三連支持一下,春人的主頁還有很多有趣的文章,歡迎小伙伴們前去點評,您的支持就是春人前進的動力!