这是我的代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <wait.h> #include <readline/readline.h> #define NUMPIPES 2 int main(int argc,char *argv[]) { char *bBuffer,*sPtr,*aPtr = NULL,*pipeComms[NUMPIPES],*cmdArgs[10]; int fdPipe[2],pCount,aCount,i,status,lPids[NUMPIPES]; pid_t pid; pipe(fdPipe); while(1) { bBuffer = readline("Shell> "); if(!strcasecmp(bBuffer,"exit")) { return 0; } sPtr = bBuffer; pCount = -1; do { aPtr = strsep(&sPtr,"|"); pipeComms[++pCount] = aPtr; } while(aPtr); for(i = 0; i < pCount; i++) { aCount = -1; do { aPtr = strsep(&pipeComms[i]," "); cmdArgs[++aCount] = aPtr; } while(aPtr); cmdArgs[aCount] = 0; if(strlen(cmdArgs[0]) > 0) { pid = fork(); if(pid == 0) { if(i == 0) { close(fdPipe[0]); dup2(fdPipe[1],STDOUT_FILENO); close(fdPipe[1]); } else if(i == 1) { close(fdPipe[1]); dup2(fdPipe[0],STDIN_FILENO); close(fdPipe[0]); } execvp(cmdArgs[0],cmdArgs); exit(1); } else { lPids[i] = pid; /*waitpid(pid,&status,0); if(WIFEXITED(status)) { printf("[%d] TERMINATED (Status: %d)\n",pid,WEXITSTATUS(status)); }*/ } } } for(i = 0; i < pCount; i++) { waitpid(lPids[i],0); if(WIFEXITED(status)) { printf("[%d] TERMINATED (Status: %d)\n",lPids[i],WEXITSTATUS(status)); } } } return 0; }
(代码更新,以反映他提出的两个答案下面的变化,它仍然不工作,因为它应该…)
以下是测试用例:
nazgulled ~/Projects/SO/G08 $ls -l total 8 -rwxr-xr-x 1 nazgulled nazgulled 7181 2009-05-27 17:44 a.out -rwxr-xr-x 1 nazgulled nazgulled 754 2009-05-27 01:42 data.h -rwxr-xr-x 1 nazgulled nazgulled 1305 2009-05-27 17:50 main.c -rwxr-xr-x 1 nazgulled nazgulled 320 2009-05-27 01:42 makefile -rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog -rwxr-xr-x 1 nazgulled nazgulled 9276 2009-05-27 17:21 prog.c -rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o -rwxr-xr-x 1 nazgulled nazgulled 16 2009-05-27 17:19 test nazgulled ~/Projects/SO/G08 $./a.out Shell> ls -l|grep prog [4804] TERMINATED (Status: 0) -rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog -rwxr-xr-x 1 nazgulled nazgulled 9276 2009-05-27 17:21 prog.c -rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o
问题是我应该回到我的shell之后,我应该看到“Shell>”等待更多的输入.您还可以注意到,您没有看到类似于“[4804] TERMINATED(Status:0)”(但使用不同的pid)的消息,这意味着第二个进程没有终止.
我认为这与grep有关,因为这有用:
nazgulled ~/Projects/SO/G08 $./a.out Shell> echo q|sudo fdisk /dev/sda [4838] TERMINATED (Status: 0) The number of cylinders for this disk is set to 1305. There is nothing wrong with that,but this is larger than 1024,and could in certain setups cause problems with: 1) software that runs at boot time (e.g.,old versions of LILO) 2) booting and partitioning software from other OSs (e.g.,DOS FDISK,OS/2 FDISK) Command (m for help): [4839] TERMINATED (Status: 0)
你可以很容易地看到两个“终止”消息…
那么我的代码怎么了?
解决方法
即使您的管道的第一个命令退出(并且thust关闭stdout =〜fdPipe [1]),父进程仍然有fdPipe [1]打开.
因此,管道的第二个命令有一个stdin =〜fdPipe [0],它永远不会得到EOF,因为管道的另一个端点仍然是打开的.
您需要为每个|创建一个新的管道(fdPipe),并确保关闭父项中的两个端点;即
for cmd in cmds if there is a next cmd pipe(new_fds) fork if child if there is a prevIoUs cmd dup2(old_fds[0],0) close(old_fds[0]) close(old_fds[1]) if there is a next cmd close(new_fds[0]) dup2(new_fds[1],1) close(new_fds[1]) exec cmd || die else if there is a prevIoUs cmd close(old_fds[0]) close(old_fds[1]) if there is a next cmd old_fds = new_fds if there are multiple cmds close(old_fds[0]) close(old_fds[1])
另外,为了更安全,在执行任何关闭和dup2操作之前,应该处理fdPipe和{STDIN_FILENO,STDOUT_FILENO}重叠的情况.如果有人设法用stdin或stdout关闭来启动shell,这可能会发生,并且会导致这里的代码很混乱.
编辑
fdPipe1 fdPipe3 v v cmd1 | cmd2 | cmd3 | cmd4 | cmd5 ^ ^ fdPipe2 fdPipe4
除了确保关闭管道在父级中的端点之外,我试图说明fdPipe1,fdPipe2等不能是同一个pipe().
/* suppose stdin and stdout have been closed... * for example,if your program was started with "./a.out <&- >&-" */ close(0),close(1); /* then the result you get back from pipe() is {0,1} or {1,0},since * fd numbers are always allocated from the lowest available */ pipe(fdPipe); close(0); dup2(fdPipe[0],0);
我知道你现在不用close(0),但最后一段警告你要注意这种情况.
编辑
以下对您的代码的最小更改使其在您提到的特定故障案例中起作用:
@@ -12,6 +12,4 @@ pid_t pid; - pipe(fdPipe); - while(1) { bBuffer = readline("Shell> "); @@ -29,4 +27,6 @@ } while(aPtr); + pipe(fdPipe); + for(i = 0; i < pCount; i++) { aCount = -1; @@ -72,4 +72,7 @@ } + close(fdPipe[0]); + close(fdPipe[1]); + for(i = 0; i < pCount; i++) { waitpid(lPids[i],0);
这对于管道中的多个命令不起作用;为此,你需要这样的东西:(未经测试,因为你必须修复其他的东西)
@@ -9,9 +9,7 @@ int main(int argc,char *argv[]) { char *bBuffer,*cmdArgs[10]; - int fdPipe[2],lPids[NUMPIPES]; + int fdPipe[2],fdPipe2[2],lPids[NUMPIPES]; pid_t pid; - pipe(fdPipe); - while(1) { bBuffer = readline("Shell> "); @@ -32,4 +30,7 @@ aCount = -1; + if (i + 1 < pCount) + pipe(fdPipe2); + do { aPtr = strsep(&pipeComms[i]," "); @@ -43,11 +44,12 @@ if(pid == 0) { - if(i == 0) { - close(fdPipe[0]); + if(i + 1 < pCount) { + close(fdPipe2[0]); - dup2(fdPipe[1],STDOUT_FILENO); + dup2(fdPipe2[1],STDOUT_FILENO); - close(fdPipe[1]); - } else if(i == 1) { + close(fdPipe2[1]); + } + if(i != 0) { close(fdPipe[1]); @@ -70,17 @@ } } + + if (i != 0) { + close(fdPipe[0]); + close(fdPipe[1]); + } + + fdPipe[0] = fdPipe2[0]; + fdPipe[1] = fdPipe2[1]; + } + + if (pCount) { + close(fdPipe[0]); + close(fdPipe[1]); }