Linux C:文件描述符、IO重定向、恢复标准输入输出
目錄
? ?
一、文件描述符??
二、IO重定向
三、重定向回終端、偽終端
四、恢復標準輸入輸出
? ?
一、文件描述符??
? ? ?在Linux中,文件描述符是一個非負整數的數據類型。是FILE結構體中的一個成員屬性。?每打開或者新建一個文件時,內核都會返回最小的且未被使用的非負整數,即文件描述符。例如,文件描述符 0,1,2,4,5...已經被該進程使用了,那么再打開一個文件返回的文件描述符就是3,再打開一個新文件就是6。如果文件描述符被關閉,那么文件描述符在下一次可能會重新被打開。
? ? ?FILE結構體大致如下
-----------FILE Structure--------- char fbuf[SIZE]; int counter,index.......... 一些其它屬性 int fd; //文件描述符Linux 為每個進程創建了文件描述符表(fd, 文件指針),其中文件指針,指向了系統級的打開文件表,通過該指針可以獲取到文件偏移量和i-node指針信息,再通過文件偏移量和i-node指針,可以定位到文件系統中的i-node表,從而找到物理硬盤上的文件。換言之,在操作系統之上,通過進程號和文件描述符就可以定位到具體的文件。這個關系是一對多的,因為一個對象可以被多個指針指向,而一個指針只能指向一個對象。這就意味著,多個進程級的文件描述符可能會指向同一個系統級打開文件表,多個打開文件表項可能指向同一個i-node表。
二、IO重定向
在sh進程中有3個用于終端的IO文件流: stdin(標準輸入),stdout(標準輸出),stderr(標準錯誤)。
這3個流實際上指向文件結構體的指針, FILE *?stdin , stdout ,stderr;
它們指向的FILE的區別在于它們的文件描述符分別是STDIN_FILENO, STDOUT_FILENO,STDERR_FILENO分別對應的值是0,1,2。標準輸入默認來源于鍵盤,標準輸出、標準錯誤的目標默認是屏幕。改變它們的來源或者目標就叫IO重定向。對應linux的操作符是? ?"<","1>","2>"。
所以通常程序的缺省情況,fd通常就已經打開了3個,如果想改變輸入流或者輸出流到文件,那么就要關閉對應的文件描述符。當重新open文件時,系統就會返回最小的且未被使用的文件描述符。
例如在filename.txt寫入8878 . ,當關閉文件描述符0時,在打開文件,調用scanf函數就不再從鍵盤中輸入,而把filename.txt中的內容當作輸入。
#include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include <unistd.h> int main(){close(0);int fd = open("filename.txt",O_RDONLY);int item;scanf("%d",&item);printf("format= %d",item); }關于scanf。當FILE結構體中fbuf為空時,才會向內核中發出read系統調用,通過文件描述符為0 和進程號找到對應的文件,把文件數據讀到fbuf中。所以哪個文件獲取到了文件描述符0,那么標準輸入的來源就是這個文件。同理,哪個文件獲取到了文件描述符1? (2),那么標準輸出(錯誤)的目標就是這個文件。
三、重定向回終端、偽終端
? ? ? 終端和偽終端通常包含屏幕和鍵盤。屏幕輸出前,需要通常保存在/dev/ttyX? 文件下 ,鍵盤輸入后,輸入的內容通常保存在 /dev/pts/# 下。
確定具體文件描述符打開的對應文件
例如在gdb調試代碼時
通過? ?ps? -ef |grep a.out 找到進程號
查詢? ?/proc/[進程號]/fd? 中的內容? (需要root賬號才可以登錄fd目錄)
查看到我的偽終端是:
/dev/pts/2
或者用 tty 命令查看偽終端
yu'shhi標準輸入重定向回鍵盤:
close(0);int fd = open("/dev/pts/2",O_RDONLY);四、恢復標準輸入輸出
在/dev目錄下有/dev/stdin?文件,stdout文件,stderr文件
注意:標準輸入文件/dev/stdin是個鏈接文件!,其他也一樣,它們存放的是文件描述符的地址,鏈接文件類似指針,而標準IO文件類似于雙重指針。文件描述符在Linux系統中也是一個鏈接文件。當close([文件描述符時]),其實刪除的時文件描述符對應的鏈接文件。而/dev/stdin固定鏈接每個 /proc/self/fd/0,當0被刪除了之后,/dev/stdin也就鏈接成了空文件。
也就是說下述代碼得到結果并沒有重新獲得鍵盤輸入:
#include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include <unistd.h> int main(){close(0); /**文件 /proc/[pid]/fd/0 消失*****/ int fd = open("filename.txt",O_RDONLY); /**文件 /proc/[pid]/fd/0 鏈接上了filename.txt */ int item;scanf("%d",&item); /*stdin 找到/proc/[pid]/fd/0 中讀數據*/close(0); /*文件 /proc/[pid]/fd/0消失,/dev/stdin為空鏈接*///這樣做的意圖是 /dev/stdin -> /proc/self/0 -> /dev/stdin 嗎??? 顯然目的不是這樣的int fd = open("/dev/stdin",O_RDONLY); /*打開空鏈接文件,并非是終端文件*/scanf("%d",&item); printf("format= %d",item); }所以如果要保證代碼的通用性,變更stdin鏈接之前需要把終端文件用新的文件描述符保存起來。
(1)? int fd = dup(oldfd);? ?,? ? ?int fd= dup2(oldfd,newfd);
當調用dup函數時,內核在進程中創建一個新的文件描述符,此描述符是當前可用文件描述符的最小數值,這個文件描述符指向oldfd所擁有的文件表項。
dup2和dup的區別就是可以用newfd參數指定新描述符的數值。
APUE用另外一個種方法說明了這個問題:
實際上,調用dup(oldfd)等效于,fcntl(oldfd, F_DUPFD, 0)
而調用dup2(oldfd, newfd)等效于,close(oldfd);fcntl(oldfd, F_DUPFD, newfd);
查看如下代碼:
#include<fcntl.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include <unistd.h> int main(){int fd2=dup(0) ; //復制文件描述符0對應的表項給fd2 = 3,此時fd2 ,和0 都指向終端close(0); //關閉文件描述符0int fd = open("filename.txt",O_RDONLY); //filename.txt獲取文件描述符0 int item;scanf("%d",&item); //從標準輸入0中讀數據printf("format1= %d\n",item); //輸出filename.txt中讀出來的內容dup2(fd2,0); //復制文件描述符fd2表項給0,此時0重新指向終端,// fopen("/dev/stdin", "r+"); scanf("%d",&item); //同時意味著/dev/stdin 也間接指向了終端printf("format2= %d\n",item); }總結
以上是生活随笔為你收集整理的Linux C:文件描述符、IO重定向、恢复标准输入输出的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux C :系统调用-fork,w
- 下一篇: Linux C:管道的实现原理,命名管道