1. 程式人生 > >淺談檔案描述符1和2

淺談檔案描述符1和2

轉自:http://www.361way.com/filedesc-1-2/5536.html

在linux系統中, 我們知道有 標準輸入 , 標準輸出 , 和標準錯誤, 並且都分別對應著0, 1, 2這三個檔案描述符, 在每個程序誕生之際,就已經隨著伴隨左右,通過以下命令就能看出來:

filedesc

從上圖可以很清楚的看出來, 0, 1, 2都分別繫結到/dev/pts/1(根據實際情況來看), /dev/pts/1是我這邊的終端裝置檔案, 正因為這樣的繫結關係, 所以我們才能在螢幕中看到標準輸出/標準錯誤, 在系統方面, 這三個數字已經稱為約定俗成的規定了, 而且從系統選擇檔案描述符的套路也能看出這點。

系統在分配新的檔案描述符時, 總是從最小並且未被使用開始.所以從上面的種種因素中, 我們能夠得出一個猜想, 如果我們改變檔案描述符 1,2的指向, 是不是就能實現重定向符 ">" 的作用呢? 答案是肯定的!

  1. [[email protected] test]# touch 123
  2. [[email protected] test]# ls # 在未改變標準輸出的 ls 結果
  3. 123
  4. [[email protected] test]# exec 10>&1 # 用exec 將檔案描述符1原有的繫結結果暫存到 檔案描述符10
  5. [[email protected] test]# exec 1>stdout.log # 用exec 將檔案描述符1繫結到stdout.log
  6. [[email protected] test]# ll /proc/$$/fd
  7. [[email protected] test]# echo 'www.361way.com'
  8. ### 以上的命令均無輸出 ###
  9. [[email protected] test]# cat stdout.log # 這個有一個標準錯誤出現,這個提示很有意思。
  10. cat: stdout.log: input file is output file
  11. [[email protected] test]# exec 1>&10 # 將檔案描述符1 還原回來
  12. [[email protected] test]# ls
  13. 123 stdout.log
  14. [[email protected] test]# cat stdout.log # 檢視剛才繫結的檔案, 裡面的結果就是剛才 ls和echo的結果
  15. total 0
  16. lrwx------ 1 root root 64 Aug 16 15:42 0 -> /dev/pts/1
  17. l-wx------ 1 root root 64 Aug 16 15:42 1 -> /tmp/test/stdout.log # 對應上面的exec 1>stdout.log
  18. lrwx------ 1 root root 64 Aug 16 15:44 10 -> /dev/pts/1
  19. lrwx------ 1 root root 64 Aug 16 15:42 2 -> /dev/pts/1
  20. lrwx------ 1 root root 64 Aug 16 15:44 255 -> /dev/pts/1
  21. www.361way.com

上面的實驗執行的時候,注意以下幾點:

1、執行exec 1>stdout.log後,此時會發現vim、nano、cat、less、more、ls 等涉及到標準輸出,在螢幕上顯示的命令都不能正常使用了;

2、不通過中間描述符10,是無法將原標準輸出改回來的。不過也並不是執行這個修改後就會一直有問題,斷開重新登入後或新開一個連線exec 1>stdout.log就失效了,因為新開時,0、1對應的可能是/dev/pts/2或/dev/pts/3 ;

3、/proc/$$/fd下的0、1、10等,是無法通過ln命令連結到/dev/pts/num的,也無法隨意進行刪除 。

  1. [[email protected] fd]# pwd
  2. /proc/48226/fd
  3. [[email protected] fd]# ll
  4. total 0
  5. lrwx------ 1 root root 64 Aug 16 15:55 0 -> /dev/pts/1
  6. lrwx------ 1 root root 64 Aug 16 15:55 1 -> /dev/pts/1
  7. lrwx------ 1 root root 64 Aug 16 15:56 10 -> /dev/pts/1
  8. lrwx------ 1 root root 64 Aug 16 15:55 2 -> /dev/pts/1
  9. lrwx------ 1 root root 64 Aug 16 15:55 255 -> /dev/pts/1
  10. [[email protected] fd]# pwd
  11. /proc/48226/fd
  12. [[email protected] fd]# ln -s /dev/pts/1 100
  13. ln: failed to create symbolic link ‘100’: No such file or directory
  14. [[email protected] fd]# rm -rf 10
  15. rm: cannot remove ‘10’: Operation not permitted

對於標準輸出是這樣, 對於標準錯誤也是這樣, 如果對標準錯誤執行上面的實驗, 會發現一個神奇的問題,怎麼神奇, 看程式碼:

stderr-magic

為什麼會有這樣的結果, 如果將錯誤輸出重定向會導致這個結果, 那是不是就能說明我們看到的顯示,其實都是標準錯誤的內容呢?

從檔案stderr.log的輸出, 我們已經能夠確定, 我們登入終端看到的命令提示符什麼的, 都是通過標準錯誤展示在我們面前, 這樣似乎和我們的理解有點出入啊, 為什麼不用標準輸出呢, 多讓人誤解呀? 其實當我們瞭解到系統標準IO的三種緩衝模式就能知道為什麼系統會通過標準錯誤展示:

三種緩衝模式:

  1. 全緩衝: 直到緩衝區被填滿,才呼叫系統I/O函式, (一般是針對檔案)
  2. 行緩衝: 遇到換行符就輸出(標準輸出)
  3. 無緩衝: 沒有緩衝區,資料會立即讀入或者輸出到外存檔案和裝置上(標準錯誤)

一般來說, 無緩衝是最慢的, 但是存在即是合理, 它的作用就是為了讓內容儘快的展示, 所以現在我們不難理解為什麼命令提示符需要用標準錯誤輸出了, 因為我們總不能等一個回車嘛~ 

如上面所說的, 我們可以通過改變檔案描述符1/2的繫結, 來實現重定向, 實際上, 系統也是用這樣類似的方法, 不過系統一般是用dup2/fcntl函式,這些又是一個大話題了