C 递归管道()调用后I/O挂起

C 递归管道()调用后I/O挂起,c,recursion,pipe,C,Recursion,Pipe,这是我上一篇文章的后续文章。我正在编写一个linux shell,因此我需要处理用户输入多个管道命令的可能性。除了在最后一个命令上调用execvp()后I/O挂起之外,它几乎可以正常工作。我的提示再也不会出现,我必须按住ctrl+C键才能离开shell。我不认为这是一个无限循环的发生,而是我没有正确地关闭我的流。我想不出做这件事的正确方法 示例-如果我根本不使用管道,则外壳将正确运行: ad@ubuntu:~/Documents$ gcc mash.c -o mash ad@ubuntu:~/D

这是我上一篇文章的后续文章。我正在编写一个linux shell,因此我需要处理用户输入多个管道命令的可能性。除了在最后一个命令上调用execvp()后I/O挂起之外,它几乎可以正常工作。我的提示再也不会出现,我必须按住ctrl+C键才能离开shell。我不认为这是一个无限循环的发生,而是我没有正确地关闭我的流。我想不出做这件事的正确方法

示例-如果我根本不使用管道,则外壳将正确运行:

ad@ubuntu:~/Documents$ gcc mash.c -o mash
ad@ubuntu:~/Documents$ ./mash
/home/ad/Documents> ls
a.out     bio1.odt  blah.cpp       controller.txt  mash.c
bio1.doc  blahblah.txt  Chapter1Notes.odt  mash
/home/ad/Documents> 
但是,如果我键入:

/home/ad/Documents> ls -l | grep sh
-rwxr-xr-x 1 ad ad  13597 2011-09-26 00:03 mash
-rw-r--r-- 1 ad ad   3060 2011-09-25 23:58 mash.c
提示不再出现。main()最初使用stdin和stdout调用execute()

谢谢你的时间

代码:

#包括
#包括
#包括
#包括
#包括
int MAX_PATH_LENGTH=1024//要显示的最大路径长度。
int BUF_LENGTH=1024;//用于存储用户输入的缓冲区长度
char*delims=“\n”//用于标记用户输入的分隔符。
管道内常数读数=0;
const int PIPE_WRITE=1;
无效执行(字符**argArray,int read\u fd,int write\u fd){
dup2(read_fd,0);
dup2(write_fd,1);
//仅输入换行符时出现问题
char**pA=argArray;
int i=0;
而(*pA!=NULL){
如果(strcmp(argArray[i],“”)=0{
int输出=打开(argArray[i+1],O|u RDWR | O|u CREAT);
pid_t pid=fork();
如果(pid==0){
dup2(输出,1);
关闭(输出);
argArray[i]=0;
execvp(argArray[0],&argArray[0]);
printf(“重定向输出时出错。\n”);
出口(1);
}
关闭(输出);
等待(空);
}
else if(strcmp(argArray[i],“|”)==0){
int-fds[2];
管道(fds);
pid_t pid=fork();
如果(pid==0){
dup2(fds[PIPE_WRITE],1);
关闭(fds[管道读取]);
关闭(fds[管道写入]);
argArray[i]=0;
execvp(argArray[0],&argArray[0]);
printf(“%s:未找到命令。\n”,argArray[0]);
出口(1);
}否则{
dup2(fds[PIPE_READ],0);
执行(&argArray[i+1],0,1);
关闭(fds[管道读取]);
关闭(fds[管道写入]);
等待(pid);
printf(“herp\n”);
}
}
*pA++;
i++;
}
pid_t pid=vfork();
如果(pid==0){
execvp(argArray[0],&argArray[0]);
printf(“%s:未找到命令。\n”,argArray[0]);
出口(1);
}
否则{
等待(空);
}
}
int main(){
字符路径[最大路径长度];
字符buf[buf_长度];
字符*长度[BUF_长度];
/**
*“欢迎”消息。执行mash时,当前工作目录
*显示时后跟>。例如,如果用户位于/usr/lib/,则
*mash将显示:
*/usr/lib/>
**/
getcwd(路径,最大路径长度);
printf(“%s>”,路径);
fflush(stdout);
/**
*在等待用户输入时无限循环。
*解析输入并再次显示“欢迎”消息。
**/ 
而(1){
fgets(buf、buf_长度、标准尺寸);
char*tokenPtr=NULL;
int i=0;
tokenPtr=strtok(buf,delims);
if(strcmp(tokenPtr,“exit”)==0){
出口(0);
}
else if(strcmp(tokenPtr,“cd”)==0){
tokenPtr=strtok(NULL,delims);
如果(chdir(tokenPtr)!=0){
printf(“未找到路径。\n”);
}
getcwd(路径,最大路径长度);
}
否则如果(strcmp(tokenPtr,“pwd”)==0){
printf(“%s\n”,路径);
}
否则{
while(tokenPtr!=NULL){
strArray[i++]=tokenPtr;
tokenPtr=strtok(NULL,delims);
}
执行(strArray,0,1);
}
bzero(strArray,sizeof(strArray));//清除数组
printf(“%s>”,路径);
fflush(stdout);
}
}
此行-
dup2(fds[PIPE\u READ],0)-使用引用管道的描述符覆盖当前stdin文件描述符。一旦pipe命令完成,任何从stdin读取的尝试都将失败

该行-
fgets(buf,buf_长度,标准输入)-不检查错误条件

最后—在开始第二个进程之前,等待管道中的第二个进程完成。这就是造成你僵局的原因;“grep”命令正在等待输入,但您尚未执行“ls”命令。您等待grep命令完成,但它无法完成,因为它正在等待输入

在代码的最新版本中:调用execute()函数时,它扫描参数并找到管道;然后它分叉并运行第一个命令(“ls”):

然后它递归,再次调用execute():

}其他{
dup2(fds[PIPE_READ],0);

execute(&argArray[i+1],0,1);//这两行的参数顺序错误:

dup2(0, read_fd);
dup2(1, write_fd);
你应该写:

dup2(read_fd, 0);
dup2(write_fd, 1);
或:


但是,无论是修订版还是原始版,执行调用为:

 execute(strArray, 0, 1);

这意味着这两个
dup2()
调用什么都不做(将0复制到0和1复制到1)。

没错,我确实需要用fgets()检查错误条件行。不过,关于死锁,grep正在获取输入,甚至将其写入终端。不过,死锁是在它写入之后发生的。好的,我错误地遵循了代码路径。不过,我发现代码中有一些地方值得怀疑。首先,我不确定在v之后操作文件描述符是否有效fork()-尝试改用fork()(并在工作后将其更改为vfork())。正如我在回答中指出的,覆盖fd#0的dup2行也有问题。除此之外,t
            close(fds[PIPE_READ]);
            close(fds[PIPE_WRITE]);
            wait(pid);   // <--- WRONG, compile with -Wall to see why
            printf("herp\n");
        }
dup2(0, read_fd);
dup2(1, write_fd);
dup2(read_fd, 0);
dup2(write_fd, 1);
dup2(read_fd,  STDIN_FILENO);
dup2(write_fd, STDOUT_FILENO);
 execute(strArray, 0, 1);