2010-11-19 17 views
2

我目前正在編寫我自己的shell作爲一個類的項目,並讓所有的東西都可以工作。我的問題是我的管道,有時他們工作,有時,他們只是掛起,直到我打斷他們。我已經完成了對此的研究,並且它看起來正在寫入stdin的函數沒有從第一個進程接收EOF;通常我已經知道問題是管道沒有關閉,但是根據我的代碼,情況並非如此(據我所知)。編寫自己的shell - 處理某些管道時掛起的代碼 - 在C

所有重定向作品以及它們的任何變型:

  • ls -l > file1
  • wc <file1> file2

以下管道命令的工作:

  • w | head -n 4
  • w | head -n 4 > file1

這不起作用:ls | grep file1它顯示了正確的輸出,並永遠不會結束,除非一箇中斷信號由用戶發送給它。 ls | grep file1 > file2也不起作用。它掛起而不顯示輸出,創建file2,但從不寫入。

無論如何,我希望有一些我錯過了別人可以注意到的東西;我一直在這一段時間。讓我知道是否還有我可以提供的代碼。我在下面發佈的代碼是主文件,沒有任何刪除。

/* 
* This code implemenFts a simple shell program 
* At this time it supports just simple commands with 
* any number of args. 
*/ 

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

#include "input.h" 
#include "myShell.h" 
#include "BackgroundStack.h" 

/* 
* The main shell function 
*/ 
main() { 
    char *buff[20]; 
    char *inputString; 

    BackgroundStack *bgStack = malloc(sizeof(BackgroundStack)); 
    initBgStack(bgStack); 

    struct sigaction new_act; 
    new_act.sa_handler = sigIntHandler; 
    sigemptyset (&new_act.sa_mask); 
    new_act.sa_flags = SA_RESTART; 
    sigaction(SIGINT, &new_act, NULL); 

    // Loop forever 
    while(1) { 
     const char *chPath; 

     doneBgProcesses(bgStack); 

     // Print out the prompt and get the input 
     printPrompt(); 

     inputString = get_my_args(buff); 
     if (buff[0] == NULL) continue; 

     if (buff[0][0] == '#') continue; 

     switch (getBuiltInCommand(buff[0])) { 
      case EXIT: 
       exit(0); 
       break; 
      case CD: 
       chPath = (buff[1]==NULL) ? getenv("HOME") : buff[1]; 
       if (chdir(chPath) < 0) { 
        perror(": cd"); 
       } 
       break; 
      default: 
       do_command(buff, bgStack); 
     } 

     //free up the malloced memory 
     free(inputString); 
    }// end of while(1) 
} 

static void sigIntHandler (int signum) {} 

/* 
* Do the command 
*/ 
int do_command(char **args, BackgroundStack *bgStack) { 
    int status, statusb; 
    pid_t child_id, childb_id; 
    char **argsb; 
    int pipes[2]; 

    int isBgd = isBackgrounded(args); 
    int hasPipe = hasAPipe(args); 

    if (isBgd) removeBackgroundCommand(args); 
    if (hasPipe) { 
     int cmdBi = getSecondCommandIndex(args); 
     args[cmdBi-1] = NULL; 
     argsb = &args[cmdBi]; 
     pipe(pipes); 
    } 

    // Fork the child and check for errors in fork() 
    if((child_id = fork()) == -1) { 
     switch(errno) { 
      case EAGAIN: 
       perror("Error EAGAIN: "); 
       return; 
      case ENOMEM: 
       perror("Error ENOMEM: "); 
       return; 
     } 
    } 

    if (hasPipe && child_id != 0) { 
     childb_id = fork(); 
     if(childb_id == -1) { 
      switch(errno) { 
       case EAGAIN: 
        perror("Error EAGAIN: "); 
        return; 
       case ENOMEM: 
        perror("Error ENOMEM: "); 
        return; 
      } 
     } 
    } 

    if(child_id == 0 || (childb_id == 0 && hasPipe)) { 
     if (child_id != 0 && hasPipe) args = argsb; 
     if (child_id == 0 && isBgd) { 
      struct sigaction new_act; 
      new_act.sa_handler = SIG_IGN; 
      sigaction(SIGINT, &new_act, 0); 
     } 

     if (child_id == 0 && hasPipe) { 
      if (dup2(pipes[1], 1) != 1) fatalPerror(": Pipe Redirection Output Error"); 
      close(pipes[0]); 
      close(pipes[1]); 
     } 
     if (child_id != 0 && hasPipe) { 
      if (dup2(pipes[0], 0) != 0) fatalPerror(": Pipe Redirection Input Error"); 
      close(pipes[0]); 
      close(pipes[1]); 
      waitpid(child_id, NULL, 0); 
     } 

     if ((child_id != 0 && hasPipe) || !hasPipe) { 
      if (hasAReOut(args)) { 
       char outFile[100]; 
       getOutFile(args, outFile); 

       int reOutFile = open(outFile, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE); 
       if (reOutFile<0) fatalPerror(": Redirection Output Error"); 

       if (dup2(reOutFile,1) != 1) fatalPerror(": Redirection Output Error"); 
       close(reOutFile); 
      } 
     } 

     if ((child_id == 0 && hasPipe) || !hasPipe) { 
      if (hasAReIn(args)) { 
       char inFle[100]; 
       getInFile(args, inFle); 

       int reInFile = open(inFle, O_RDWR); 
       if (reInFile<0) fatalPerror(": Redirection Input Error"); 

       if (dup2(reInFile,0) != 0) fatalPerror(": Redirection Input Error"); 
       close(reInFile); 
      } else if (isBgd && !hasPipe) { 
       int bgReInFile = open("/dev/null", O_RDONLY); 
       if (bgReInFile<0) fatalPerror(": /dev/null Redirection Input Error"); 

       if (dup2(bgReInFile,0) != 0) fatalPerror(": /dev/null Redirection Input Error"); 
       close(bgReInFile); 
      } 
     } 

     // Execute the command 
     execvp(args[0], args); 
     perror(args[0]); 

     exit(-1); 
    } 

    // Wait for the child process to complete, if necessary 
    if (!isBgd) waitpid(child_id, &status, 0); 
    else if (!hasPipe) { 
     printf("Child %ld started\n", (long)child_id); 
     BackgroundProcess *bgPrs = malloc(sizeof(BackgroundProcess)); 
     bgPrs->pid = child_id; 
     bgPrs->exitStatus = -1; 

     addProcessToBgStack(bgStack, bgPrs); 
    } 
    if (hasPipe) waitpid(childb_id, &statusb, 0); 
    if (WIFSIGNALED(status) && !isBgd) printf("Child %ld terminated due to signal %d\n", (long)child_id, WTERMSIG(status)); 
    if (hasPipe && WIFSIGNALED(statusb)) printf("Child %ld terminated due to signal %d\n", (long)childb_id, WTERMSIG(status)); 

} // end of do_command 
+0

如果有人想看到工作代碼,這是我有:http://pastebin.com/VevMrng9 – 2010-11-20 19:09:08

回答

7

的第二個孩子應該等待第一個孩子退出 - 它應該只是開始馬上運行(這將阻塞,直到有輸出由第一個孩子在管道生產),所以刪除由childb執行的waitpid()。相反,父進程應該等待兩個子進程(或者也許只是第二個進程)。 (事實上​​,正如JeremyP所指出的那樣,waitpid()呼叫無論如何都是失敗的,因爲childb不是child的父母)。

不過,您的問題是,父進程主要是將打開的文件描述符添加到管道中。註釋// Wait for the child process to complete, if necessary前右,父進程應該關閉其管道文件描述符:

close(pipes[0]); 
close(pipes[1]); 

父打開的文件描述符意味着子grep過程永遠不會看到EOF,所以它不會退出。

+0

+1,這解釋了爲什麼grep永遠不會終止。 – JeremyP 2010-11-19 13:37:06

+0

謝謝你,正如你所建議的那樣,我在父母等待其子進程之前關閉了管道(如果該命令包含管道)它工作得很好。我忽略記得管子在三個不同的地方開着。 – 2010-11-20 19:04:23

1

我不知道答案,但我發現了一個問題。

你會同意,只爲兩個子流程,

if(child_id == 0 || (childb_id == 0 && hasPipe)) 

的情況是真實的,但if語句塊中你有這樣的:

if (child_id != 0 && hasPipe) { 
     if (dup2(pipes[0], 0) != 0) fatalPerror(": Pipe Redirection Input Error"); 
     close(pipes[0]); 
     close(pipes[1]); 
     waitpid(child_id, NULL, 0); 
    } 

waitpid()通話是不正確因爲它是從第二個孩子中調用來等待第一個孩子的。這可能與ECHILD失敗,因爲第一個孩子不是第二個孩子的孩子。

至於你真正的問題,我懷疑這與grep命令在其輸入關閉之前不會終止有關。可能會出現一些僵局,阻止這種情況的發生。您需要在調試器中運行此操作,或者進行一些登錄以查看父進程掛起的位置。

編輯

CAF的答案告訴我們一切。

我假設grep的輸入被關閉了,因爲ls在它終止時會關閉它的輸出,但是當然父進程也會打開grep的輸入文件描述符。使用head的版本正常工作,因爲head -n 4在四行後終止,而不管其輸入文件描述符是否關閉。

+0

我不太清楚爲什麼我覺得第二個孩子需要等待第一個孩子,我想這只是我試圖修復這個程序的瘋狂行爲。我繼續去除它。 – 2010-11-20 19:08:04