成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Unix的標準I/O與重定向的若干概念解析

系統 其他OS 系統運維
Unix默認從文件描述符0讀取數據,寫數據到文件描述符1,將錯誤信息輸出到文件描述符2。重定向標準輸入、標準輸出和錯誤輸出意味著改變文件描述符0、1和2的連接。管道是內核中的一個數據隊列,其每一端連接一個文件描述符。程序通過pipe系統調用來創建管道。

標準I/O與重定向的若干概念

3個標準文件描述符

所有的Unix工具都使用文件描述符0、1和2。如下圖所示,標準輸入文件的描述符是0,標準輸出的文件描述符是1,標準錯誤輸出的文件描述符則是2。Unix假設文件描述符0、1和2都已經被打開,可以分別進行讀、寫和寫的操作。

 

重定向I/O的是shell而不是程序

通過使用輸出重定向標志,命令cmd>filename告訴shell將文件描述符1定位到文件。于是shell就將文件描述符與指定的文件連接起來。程序持續不斷地將數據寫到文件描述符1中,根本沒有意識到數據的目的地已經改變了。listargs.c展示了程序甚至沒有看到命令行中的重定向符號。

  1. #include <stdio.h> 
  2.  
  3. int main(int ac, char* av[]) { 
  4.     int i; 
  5.     printf("Number of args: %d, Args are: \n", ac); 
  6.     for(i = 0; i < ac; i++) { 
  7.         printf("args[%d] %s\n", i, av[i]); 
  8.     } 
  9.     fprintf(stderr, "This message is sent to stderr.\n"); 

 

程序listargs將命令行參數打印到標準輸出。注意listargs并沒有打印出重定向符號和文件名。

 

如上圖所示驗證了關于shell輸出重定向的一些重要概念。

  • shell并不將重定向標記和文件名傳遞給程序。
  • 重定向可以出現在命令行中的任何地方,并且在重定向標識符周圍并不需要空格來區分。例如上圖命令./listargs testing >xyz one two 2>oops也可以寫成./listargs >xyz testing one two 2>oops,如下圖所示。

 

***可用文件描述符(Lowest-Available-fd)原則

文件描述符是一個數組的索引號。每個進程都有其打開的一組文件,這些打開的文件被保持在一個數組中。文件描述符即為某文件在此數組中的索引。并且,當打開文件時,為此文件安排的文件描述符總是此數組中***可用位置的索引。

將stdin重定向到文件

考慮如何將標準輸入重定向以至可以從文件中讀取數據。更加精確的說,進程并不是從文件讀數據,而是從文件描述符讀取數據。如果將文件描述符0重定向到一個文件,那么此文件就成為標準輸入的源。

方法1:close-then-open

***種放方法是close-then-open策略,具體步驟如下:

  • 開始時,系統中采用的是典型的設置,即三種標準流是被連接到終端設備上的。輸入的數據流經過文件描述符0而輸出的流經過文件描述符1和2。
  • 接下來,調用close(0),將標準輸入與終端設備的連接切斷。
  • ***,使用open(filename, O_RDONLY)打開一個想連接到stdin上的文件。當前的***可用文件描述符是0,因此所打開的文件將被連接到標準輸入上。任何從標準輸入讀取數據的函數都將從此文件中讀取數據。

方法2:open-close-dup-close

Unix系統調用dup建立指向已經存在的文件描述符的第二個連接,這種方法需要4個步驟。

  • open(file),打開stdin將要重定向的文件。這個調用返回一個文件描述符fd,這個描述符并不是0,因為0在當前已經被打開了。
  • close(0),將文件描述符0關閉,現在文件描述符0已經空閑了。
  • dup(fd),系統調用dup(fd)將文件描述符fd做了一個復制。此處復制使用***可用的文件描述符號。因此獲得的文件描述符是0。這樣,就將磁盤文件與文件描述符0連接在一起了。
  • close(fd),使用close(fd)來關閉原始連接,只留下文件描述符0的連接。

dup在學習管道的時候非常重要,一個簡單一點的方案是將close(0)和dup(fd)結合在一起作為一個單獨的系統調用dup2。

重定向I/O:who>userlist

當輸入who>userlist時,shell運行who程序,并將who的標準輸出重定向到名為userlist的文件上。shell實現該重定向的關鍵之處在于fork和exec之間的時間間隙。在fork執行完后,子進程仍然在運行父進程也就是shell程序,并準備執行exec。exec將替換進程中運行的程序,但是它不會改變進程的屬性和進程中所有的連接。也就是說,在運行exec之后,進程的用戶ID不會改變,其優先級也不會改變,并且其文件描述符也和運行exec之前一樣。因此,利用這個原則來實現重定向標準輸出。

此時who就是子進程要執行的命令,當執行fork前,父進程的文件描述符1指向終端。當執行fork之后,子進程的文件描述符也喜歡指向終端,此時,子進程嘗試執行close(1),close(1)之后,文件描述符1成為***未用文件描述符,子進程現在再執行creat(userlist, mode)打開文件userlist,文件描述符1被連接到文件userlist。因此,子進程的標準輸出被重定向到文件userlist,子進程然后調用exec執行who。

子進程執行了who程序,于是子進程中的代碼和數據都被who程序的代碼和數據所替換了,然而文件描述符被保留下來。因為打開的文件并非是程序的代碼也不是數據,它們屬于進程的屬性,因此exec調用并不改變它們。

管道編程

管道是內核中一個單向的數據通道,管道有一個讀取端和一個寫入端,可以用來連接一個進程的輸出和另一個進程的輸入。

創建管道

使用系統調用result = pipe(int array[2])來創建管道,并將其兩端連接到兩個文件描述符。如下圖所示,array[0]為讀取數據端的文件描述符,而array[1]則為寫數據端的文件描述符。類似與open調用,pipe調用也使用***可用文件描述符。

 

程序pipedemo.c展示了如何創建管道并使用管道向自己發送數據。核心代碼如下:

  1. int len, i, apipe[2]; 
  2.     char buf[BUFSIZ]; 
  3.  
  4.     if(pipe(apipe) == -1) { 
  5.         perror("could not make pipe."); 
  6.         exit(1); 
  7.     } 
  8.  
  9.     printf("Got a pipe! It is file descriptors: {%d %d}\n", apipe[0], apipe[1]); 
  10.  
  11.     while(fgets(buf, BUFSIZ, stdin)) { 
  12.         len = strlen(buf); 
  13.         if(write(apipe[1], buf, len) != len) { 
  14.             perror("writing to pipe."); 
  15.             break; 
  16.         } 
  17.         for(i = 0; i < len; i++) { 
  18.             buf[i] = 'X'
  19.         } 
  20.         len = read(apipe[0], buf, BUFSIZ); 
  21.         if(len == -1) { 
  22.             perror("reading from pipe."); 
  23.             break; 
  24.         } 
  25.         if(write(1, buf, len) != len) { 
  26.             perror("writing to stdout"); 
  27.             break; 
  28.         } 
  29.     } 

 

數據流從鍵盤到進程,從進程到管道,再從管道到進程以及從進程回到終端。

使用fork來共享管道

當進程創建一個管道之后,該進程就有了連向管道兩端的連接。當這個進程調用fork的時候,它的子進程也得到了這兩個連向管道的連接。父進程和子進程都可以將數據寫到管道的寫數據端口,并從讀數據端口將數據讀出。但是當一個進程讀,而另一個進程寫的時候,管道的使用效率是***的。程序pipedemo2.c說明了如何將pipe和fork結合起來,創建一對通過管道來通信的進程,核心代碼如下:

  1. int pipefd[2]; 
  2.     int len; 
  3.     char buf[BUFSIZ]; 
  4.     int read_len; 
  5.  
  6.     if(pipe(pipefd) == -1) { 
  7.         oops("cannot get a pipe", 1); 
  8.     } 
  9.  
  10.     switch(fork()) { 
  11.         case -1: 
  12.             oops("cannot fork", 2); 
  13.         /*子進程*/ 
  14.         case 0: 
  15.             len = strlen(CHILD_MESS); 
  16.             while(1) { 
  17.                 if(write(pipefd[1], CHILD_MESS, len) != len) { 
  18.                     oops("write", 3); 
  19.                 } 
  20.                 sleep(5); 
  21.             } 
  22.         /*父進程*/ 
  23.         default
  24.             len = strlen(PAR_MESS); 
  25.             while(1) { 
  26.                 if(write(pipefd[1], PAR_MESS, len) != len) { 
  27.                     oops("write", 4); 
  28.                 }  
  29.                 sleep(1); 
  30.                 read_len = read(pipefd[0], buf, BUFSIZ); 
  31.                 if(read_len <= 0) { 
  32.                     break; 
  33.                 } 
  34.                 write(1, buf, read_len); 
  35.             } 
  36.     } 

 

技術細節

  • 從管道中讀取數據
    當進程試圖從管道讀取數據時,進程被掛起直到數據被寫進管道。
    當所有的寫進程關閉了管道的寫數據端時,試圖從管道中讀取數據的調用會返回0,這意味這文件的結束。
  • 向管道中寫數據
    寫入數據阻塞直到管道有空間去容納新的數據。
    如果所有的讀進程都已關閉了管道的讀數據端,那么對管道的寫入調用將會執行失敗。

總結

  • Unix默認從文件描述符0讀取數據,寫數據到文件描述符1,將錯誤信息輸出到文件描述符2。
  • 創建文件描述符的系統調用總是使用***可用文件描述符號。
  • 重定向標準輸入、標準輸出和錯誤輸出意味著改變文件描述符0、1和2的連接。
  • 管道是內核中的一個數據隊列,其每一端連接一個文件描述符。程序通過pipe系統調用來創建管道。
  • 當父進程調用fork的時候,管道的兩端都被復制到子進程中。
  • 只有有共同父進程的進程之間才可以用管道連接。

代碼

相關代碼見Github

參考

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2017-01-19 19:14:20

Linux重定向命令

2017-01-19 19:24:29

Linux重定向

2012-05-14 11:53:44

IO虛擬化

2010-05-11 13:36:50

Unix標準

2012-06-14 09:32:17

虛擬化

2020-12-09 11:10:12

shellLinux管道

2010-04-20 15:25:12

Unix操作系統

2010-05-04 14:42:33

Unix操作系統

2010-12-14 15:07:15

ICMP路由重定向

2021-03-28 08:32:58

Java

2009-06-17 17:13:40

UNIX

2009-06-17 09:11:24

Unix管道重定向

2012-02-22 21:15:41

unixIO阻塞

2018-11-05 11:20:54

緩沖IO

2013-09-17 15:13:28

IO

2011-06-15 14:33:13

2010-04-30 09:53:34

Unix系統

2017-08-08 09:17:41

301302重定向

2015-10-16 09:33:26

TCPIP網絡協議

2009-11-23 18:39:17

PHP重定向
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久久一区二区三区四区 | 亚洲欧美在线视频 | 天天插天天操 | 国产ts人妖系列高潮 | 日本免费一区二区三区四区 | 成人在线国产 | aaa国产大片 | 亚洲成人av在线播放 | 性高湖久久久久久久久aaaaa | 中文字幕国产精品 | 国产欧美精品区一区二区三区 | 精品成人在线观看 | 亚洲免费视频一区 | 成人激情视频在线播放 | 欧美综合国产精品久久丁香 | 99精品视频在线观看免费播放 | 国产成人免费视频网站视频社区 | 99视频免费在线 | 91视频国产区 | 涩色视频在线观看 | 一区二区三区精品视频 | 亚洲一区二区三区四区av | 九九热re| 国产精品成人一区二区三区夜夜夜 | 日韩视频免费 | 青青草中文字幕 | 亚洲高清免费视频 | 亚洲一级av毛片 | 永久精品| 久久久国产一区二区三区四区小说 | 国产精品久久久亚洲 | 欧美不卡在线 | 日韩视频一区二区在线 | 成人网在线看 | 精品欧美一区二区在线观看视频 | 天堂成人av | 在线视频第一页 | 91视视频在线观看入口直接观看 | 一区二区在线免费播放 | www国产成人免费观看视频 | 韩国主播午夜大尺度福利 |