2013-07-30 25 views
0

我是unix中的新成員。在下面的代碼中,我從命令行「〜$ foo last sort more」傳遞了三個參數,以便複製「〜$ last | sort | more」。我正在嘗試創建一個需要三個參數的程序(現在至少有三個參數)。父母將分叉三個進程。第一個過程將寫入管道。第二個進程將讀寫管道,第三個進程將從管道讀取並寫入stdout(終端)。第一個進程將執行「最後」,第二個進程將執行「排序」,第三個進程將執行「更多」,進程將休眠1,2和3秒以同步。我很確定我在創建管道和重定向輸入和輸出時遇到了問題。我沒有得到任何輸出到終端,但我可以看到進程已創建。我希望得到一些幫助。重定向輸出在Unix中的dup2和管道代碼中的困難

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <dirent.h> 
#include <unistd.h> 
#include <signal.h> 
#include <fcntl.h> 
#include <errno.h> 

#define FOUND  1 
#define NOT_FOUND 0 
#define FIRST_CHILD 1 
#define LAST_CHILD numargc 
#define PATH_1 "/usr/bin/" 
#define PATH_2 "/bin/" 

#define DUP_READ()        \ 
if (dup2(fdes[READ], fileno(stdin)) == -1)  \ 
      {        \ 
       perror("dup error");  \ 
       exit(4);     \ 
      }        

#define DUP_WRITE()       \ 
if (dup2(fdes[WRITE], fileno(stdout)) == -1) \ 
       {        \ 
        perror("dup error");  \ 
        exit(4);     \ 
       }        

#define CLOSE_FDES_READ() \ 
close(fdes[READ]); 

#define CLOSE_FDES_WRITE() \ 
close(fdes[WRITE]); 

#define EXEC(x, y)           \ 
if (execl(arraycmds[x], argv[y], (char*)NULL) == -1)  \ 
       {           \ 
        perror("EXEC ERROR");     \ 
        exit(5);        \ 
       } 
#define PRINT       \ 
printf("FD IN:%d\n", fileno(stdin)); \ 
printf("FD OUT:%d\n", fileno(stdout)); 

enum 
{ 
    READ, /* 0 */ 
    WRITE, 
    MAX 
}; 

int cmdfinder(char* cmd, char* path); /* 1 -> found, 0 -> not found */ 
int main (int argc, char* argv[]) 
{ 

    int numargc=argc-1; 
    char arraycmds[numargc][150]; 
    int i=1, m=0, sleeptimes=5, numfork; 
    int rc=NOT_FOUND; 
    pid_t pid; 
    int fdes[2]; 

    if(pipe(fdes) == -1) 
    { 
     perror("PIPE ERROR"); 
     exit(4); 
    } 

    while(i <= numargc) 
    { 
     memset(arraycmds[m], 0, 150); 
     rc=cmdfinder(argv[i], arraycmds[m]); 
     if (rc) 
     { 
      printf("Command found:%s\n", arraycmds[m]); 
     } 
     i++; 
     m++; 
    } 

    i=0; //array index 
    numfork=1; //fork number 

    while(numfork <= numargc) 
    { 
     if ((pid=fork()) == -1) 
     { 
      perror("FORK ERROR"); 
      exit(3); 
     } 
     else if (pid == 0) 
     { 
      /* Child */ 

      sleep(sleeptimes); 

      if (numfork == FIRST_CHILD) 
      { 
       DUP_WRITE(); 
       EXEC(i, numfork); 
      } 
      else if (numfork == LAST_CHILD) 
      { 

       DUP_READ(); 
       CLOSE_FDES_WRITE(); 
       EXEC(i, numfork); 
      } 
      else 
      { 

       DUP_READ(); 
       DUP_WRITE(); 
       CLOSE_FDES_READ(); 
       CLOSE_FDES_WRITE(); 

       EXEC(i, numfork); 
      } 
     } 
     else 
     { 
      /* Parent */ 
      printf("pid:%d\n", pid);  
      i++; 
      numfork++; 
      sleeptimes++; 
     } 
    } 

    PRINT; 
    printf("i:%d\n", i); 
    printf("numfork:%d\n", numfork); 
    printf("DONE\n");  
    return 0; 
} 


int cmdfinder(char* cmd, char* path) 
{ 
    DIR* dir; 
    struct dirent *direntry; 
    char *pathdir; 
    int searchtimes=2; 

    while (searchtimes) 
    { 
     pathdir = (char*)malloc(250); 
     memset(pathdir, 0, 250); 

     if (searchtimes==2) 
     { 
      pathdir=PATH_1; 
     } 
     else 
     { 
      pathdir=PATH_2; 
     } 

     if ((dir = opendir(pathdir)) == NULL) 
     { 
      perror("Directory not found"); 
      exit (1); 
     } 
     else 
     { 
      while (direntry = readdir(dir)) 
      { 
       if (strncmp(direntry->d_name, cmd, strlen(cmd)) == 0) 
       { 
        strcat(path, pathdir); 
        strcat(path, cmd); 
        //searchtimes--; 
        return FOUND; 
       } 
      } 
     } 
     closedir(dir); 
     searchtimes--; 
    } 
    printf("%s: Not Found\n", cmd); 
    return NOT_FOUND; 
} 

回答

0

所有的宏都比直寫它更難。特別是當他們引用局部變量時。爲了找出EXEC發生了什麼,我的眼睛必須從它使用的位置跳到它定義的位置,找出它使用的是哪個本地數組,然後跳回來看看訪​​問如何適合流程main。這是一個宏的迷宮。

哇,cmdfinder?你自己的$PATH查找,只有它是硬編碼/usr/bin:/bin?而雙哇,readdir,只是爲了找出一個文件是否存在的名字已經決定?只需stat吧!或者不要做任何事情,只要執行它並通過嘗試下一個來處理ENOENT。或者使用execlp這就是它的用途!

在主要觀點上......你沒有足夠的管道,而且你沒有關閉所有未使用的描述符。

last | sort | more是由2根管道連接的3個命令的管道。你不能用一根管子做。第一個命令應寫入第一個管道,中間命令應讀取第一個管道並寫入第二個管道,最後一個命令應讀取第二個管道。

您可以先創建兩個管道,然後完成所有的分支,這使得事情變得簡單,但是在每個子流程中都需要很多close,因爲它們都會繼承所有的管道分支。或者,您可以使用更復雜的循環,在分叉將使用它的第一個進程之前創建每個管道,並在創建相關子進程後立即關閉父進程中的每個描述符。我不想看看你會爲此使用多少個宏。

每一次成功的dup都應該跟着被複制的描述符關閉。 dup是「複製」的縮寫,而不是「移動」。完成後,您還剩下一個額外的描述符,因此不僅僅是dup2(fdes[1], fileno(stdout) - 之後還有close(fdes[1])。 (是完全健壯的,你應該檢查是否已經fdes[1]==fileno(stdout),在這種情況下,跳過dup2close。)

後續問題

不能使用一個管3點的過程,因爲不會有什麼方法來區分哪些數據應該去哪個目的地。當第一個進程寫入管道時,其他進程都試圖從中讀取數據,其中一個進程將獲取數據,但無法預測哪個進程。您需要中間進程來讀取第一個進程寫入的內容,以及讀取中間進程寫入內容的最後一個進程。

對於在fork之後被共享的文件描述符,你是半途而廢。實際的管道對象是共享的。這就是整個系統的工作原理。但是,該文件描述 - 由像1標準輸出,0爲標準輸入,等小整數指定端點 - 不連接你建議的方式。相同的管道對象可以在兩個進程中與相同的文件描述符編號相關聯,這些關聯是獨立的。在一個過程中關閉fd 1不會導致fd 1在任何其他過程中關閉,即使它們是相關的。

共享FD表,以便在一個任務密切在另一個任務的影響,是「並行線程」功能集的一部分,而不是「叉」特性集。

+0

非常感謝你。我真的很感激你花時間看我的代碼。我有幾個問題,但: 1)爲什麼我不能用了三個過程一個管? 2)據我所知,在一般的叉後,打開的文件描述符是父母和孩子之間共享。如果孩子關閉了文件描述符,那麼父文件描述符仍然是打開的,反之亦然。我的理解是否正確? 3)如何被重定向一個過程的標準輸出,以不影響其他進程的STDOUT的文件。並不是所有進程都共享相同的fd,而STDOUT是1。 – user2632477