编写自己的shell代码在处理某些pipe道时挂起 – 在C中

我目前正在编写自己的shell作为一个类的项目,并让所有东西都可以工作。 我的问题是我的pipe道,有时他们的工作,有时,他们只是挂起,直到我打断他们。 我已经做了这方面的研究,似乎得到它的stdin写入的函数没有从第一个进程接收EOF; 通常我已经知道问题是pipe道没有closures,但是根据我的代码情况并非如此(据我所知)。

所有redirect工作及其任何变化:

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

以下pipe道命令工作:

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

这是行不通的: ls | grep file1 ls | grep file1它显示正确的输出,永远不会结束,除非用户发送一个中断信号。 ls | grep file1 > file2 ls | grep file1 > file2也不起作用。 它挂起而不显示输出,创buildfile2,但从不写入。

无论如何,我希望有一些我错过了别人可以注意到的东西。 我已经在这一段时间了。 让我知道是否有任何我可以提供的代码。 我在下面发布的代码是主文件,没有任何删除。

 /* * 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 

第二个孩子不应该等待第一个孩子退出 – 它应该马上开始跑步(它会阻塞,直到第一个孩子在管道上产生一些输出),所以删除由childb执行的waitpid() 。 相反,父进程应该等待两个子进程(或者也许只是第二个进程)。 (事实上​​,正如JeremyP所指出的那样,这个waitpid()调用失败了,因为childb不是child的父母)。

不过,你的问题是,父进程主要是打开文件描述符到管道。 在注释之前// Wait for the child process to complete, if necessary ,父进程应该关闭它的管道文件描述符:

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

父文件中的打开文件描述符意味着子进程永远不会看到EOF,因此不会退出。

我不知道答案,但我发现了一个问题。

你会同意的条件

 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命令不会终止,直到它的输入被关闭。 可能会出现一些僵局,阻止这种情况的发生。 您需要在调试器中运行此操作,或者进行一些登录以查看父进程挂起的位置。

编辑

咖啡的答案告诉我们一切。

我假设grep的输入被关闭,因为ls会在终止时关闭它的输出,当然,父进程也会打开grep的输入文件描述符。 使用head的版本可以正常工作,因为head -n 4在四行之后终止,而不管其输入文件描述符是否关闭。