2014-01-24 189 views
2

我對昨天提出的現有問題感到困惑:
Recursive piping in Unix again管道()帶叉()遞歸:文件描述符處理

我重新張貼問題的代碼:

#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <stdlib.h> 

void pipeline(char * ar[], int pos, int in_fd); 
void error_exit(const char*); 
static int child = 0; /* whether it is a child process relative to main() */ 

int main(int argc, char * argv[]) { 
    if(argc < 2){ 
     printf("Usage: %s option (option) ...\n", argv[0]); 
     exit(1); 
    } 
    pipeline(argv, 1, STDIN_FILENO); 
    return 0; 
} 

void error_exit(const char *kom){ 
    perror(kom); 
    (child ? _exit : exit)(EXIT_FAILURE); 
} 

void pipeline(char *ar[], int pos, int in_fd){ 
    if(ar[pos+1] == NULL){ /*last command */ 
     if(in_fd != STDIN_FILENO){ 
      if(dup2(in_fd, STDIN_FILENO) != -1) 
       close(in_fd); /*successfully redirected*/ 
      else error_exit("dup2"); 
     } 
     execlp(ar[pos], ar[pos], NULL); 
     error_exit("execlp last"); 
    } 
    else{ 
     int fd[2]; 
     pid_t childpid; 

     if ((pipe(fd) == -1) || ((childpid = fork()) == -1)) { 
      error_exit("Failed to setup pipeline"); 
     } 
     if (childpid == 0){ /* child executes current command */ 
      child = 1; 
      close(fd[0]); 
      if (dup2(in_fd, STDIN_FILENO) == -1) /*read from in_fd */ 
       perror("Failed to redirect stdin"); 
      if (dup2(fd[1], STDOUT_FILENO) == -1) /*write to fd[1]*/ 
       perror("Failed to redirect stdout"); 
      else if ((close(fd[1]) == -1) || (close(in_fd) == - 1)) 
       perror("Failed to close extra pipe descriptors"); 
      else { 
       execlp(ar[pos], ar[pos], NULL); 
       error_exit("Failed to execlp"); 
      } 
     } 
     close(fd[1]); /* parent executes the rest of commands */ 
     close(in_fd); 
     pipeline(ar, pos+1, fd[0]); 
    } 
} 

這是存在的錯誤是:

Example: 
./prog ls uniq sort head 

gives: 
sort: stat failed: -: Bad file descriptor 

這建議是:「不要關閉文件描述符fd中的解決方案[1]和in_fd在子進程中,因爲它們已經在父進程中關閉了。「

我的困惑:(對不起,我在Linux的新手)
按我的書「開始的Linux程序設計」,當我們fork()的一個過程,那麼文件描述符也被複制。因此父母和孩子應該有不同的文件描述符。這與答案矛盾。

我嘗試:
我試圖運行此代碼我自己和我看到的問題就來了只有當我關閉這兩個進程(父母和子女)的「in_fd」文件描述符。它不依賴於fd [1]。
另外,有趣的是,如果我嘗試./prog ls sort head它可以正常工作,但是當我嘗試./prog ls sort head uniq時,它給出了head上的讀取錯誤。

我的想法:in_fd文件描述符只是這個功能的輸入int變量。看起來,即使在fork之後,只有一個文件描述符仍然是由父節點和子節點共享的。但我無法理解如何。

+0

請看我的回答,如果我無法澄清任何事情,請在那裏發表評論。 – Dipto

+0

'當in_fd == STDIN_FILENO時'dup2(in_fd,STDIN_FILENO)'有問題。您在代碼中的某個點上捕獲此問題,但在另一個點上忽略它。 –

+0

@WilliamPursell你能解釋一下,如果in_fd最初是STDIN_FILENO會導致什麼問題?謝謝! – user3154219

回答

0

當我們fork()一個進程時,那麼這個文件描述符也是 的重複。因此,家長和孩子應該有不同的文件 描述

文件描述符是一個簡單的integer。所以當它被複制時,它具有相同的值,所以它們指向相同的文件。

因此,您可以在父項中打開一個文件,並從子項訪問它。唯一可能出現的問題是,如果文件是從父母和子女都訪問的,那麼它不能保證它將訪問文件的哪個位置。爲避免這種情況,建議關閉孩子的fd並重新打開。

正如你所說的那樣,我對所說的問題做了同樣的事情,並發現這總是發生在第四個命令上。另外,dup2()關閉它正在複製的文件。在問題中,fd[1]in_fd被複制到孩子的stdinstdoutfd[1]in_fd在那一刻被關閉。沒有必要再關閉它們。

關閉已經關閉的描述符將導致錯誤。

而且由於您不知道父母或孩子是否首先執行,如果您從孩子關閉一個文件並再次從父母關閉,可能會導致問題,並且此類行爲是不可預知的。

+1

*關閉一個已經關閉的描述符可能是未定義的,我不確定。*從'man 2 close':「close()在成功時返回0,錯誤時返回-1,並且適當地設置errno 錯誤 - EBADF fd不是有效的打開文件描述符。「總是檢查你的返回碼,在這種情況下,'errno'。 – Evert

+0

是否與dup2不同?在我的書本代碼中,作者在調用dup()後明確地關閉文件描述符。 DUP(file_pipes [0]); close(file_pipes [0]); – user3154219

+0

@Evert,好吧,關閉一個已經關閉的文件給出了'EBADF',這裏就是這種情況,但我不確定爲什麼它沒有給出其他文件描述符。 – Dipto