写我自己的shell…卡在pipe道上?

在过去的几天里,我一直在试图编写我自己的shell实现,但是我似乎一直困扰着让pipe道正常工作。 我能够parsing一个线,并单独分离pipe道之间的命令(例如:LS |sorting),但似乎无法让他们pipeinput从一个到另一个。

我想我只是不明白如何正确使用dup2()和pipe道。

我现在已经包括我的代码仍然失败… :(所以卡住…

void forkAndExecute( char* arrayOfWords[] , vector<pid_t> *vectorOfPIDs , bool hasNextCmd , bool hasPrevCmd) { int fd[ 2 ]; pid_t pid; if( hasNextCmd ){ pipe(fd); } pid = fork(); //error if PID < 0 if( pid < 0 ) { cerr << ">>> fork failed >>>" << endl; exit(-1); } //child process if PID == 0 else if( pid == 0 ) { if ( hasPrevCmd ){ dup2(fd[0] , 0); close(fd[0]); close(fd[1]); } if ( hasNextCmd ){ dup2(fd[1],1); close(fd[0]); close(fd[1]); } execvp( arrayOfWords[0] , arrayOfWords ); cout << ">>> command not found >>>" << endl; //if logic reaches here, exec failed exit(0); } //parent process else{ close(fd[0]); close(fd[1]); //if( ! isLastCmd ){ //} vectorOfPIDs->push_back(pid); } 

}

第一个建议:符号常量比魔术数字要好。

 const int PIPE_READ = 0; const int PIPE_WRITE = 1; int fd[2]; pipe(fd); // Now you can refer to fd[PIPE_READ] and fd[PIPE_WRITE]. 

第二个建议:退后一步,想想你想完成什么。

你想产生两个进程,第一个进程的stdout连接到第二个进程的stdin。 对?

因此,在C语言中,这意味着您需要调用pipe ,将fd[PIPE_WRITE]传递给第一个子进程,将其fd[PIPE_READ]为1,并将fd[PIPE_READ]传递给第二个子进程,这会将其fd[PIPE_READ]到0。

只要看一下forkAndExecute'的原型就可以证明它不能这样做:

 void forkAndExecute( char* arrayOfWords[] , vector *vectorOfPIDs , bool hasNextCmd , bool hasPrevCmd); 

它只处理单个命令,并且从查看该参数列表,除非它使用恶意的全局变量,否则无法从其PrevCmd接收文件描述符或从其NextCmd接收文件描述符。

想想如何管理你需要的文件描述符,并重新设计forkAndExecute来使用它们。

一般过程会将错误处理添加到此基本过程(伪代码)中:

 pipe(fds) if (fork() is child) { dup2(fds[1], 1) close(fds[0]) close(fds[1]) exec("ls") } if (fork() is child) { dup2(fds[0], 0) close(fds[0]) close(fds[1]) exec("sort") } close(fds[0]) close(fds[1]) wait() 

先创建管道。 然后fork子进程,让它们继承它。 将文件描述符重新映射为0(stdin)和1(stdout),以便进程读取和写入相应的位置。 关闭任何剩余的文件描述符,当工作完成后,您不希望子进程看到或阻塞。 执行实际的子进程。 等他们完成,你就完成了!

好的这是为我工作。 希望这可以帮助你:

 /************************ function: void pipeCommand(char** cmd1, char** cmd2) comment: This pipes the output of cmd1 into cmd2. **************************/ void pipeCommand(char** cmd1, char** cmd2) { int fds[2]; // file descriptors pipe(fds); // child process #1 if (fork() == 0) { // Reassign stdin to fds[0] end of pipe. dup2(fds[0], STDIN_FILENO); close(fds[1]); close(fds[0]); // Execute the second command. // child process #2 if (fork() == 0) { // Reassign stdout to fds[1] end of pipe. dup2(fds[1], STDOUT_FILENO); close(fds[0]); close(fds[1]); // Execute the first command. execvp(cmd1[0], cmd1); } wait(NULL); execvp(cmd2[0], cmd2); } close(fds[1]); close(fds[0]); wait(NULL); } 

这里有一个关于UNIX管道的教程,特别是关于如何在一个类似shell的体系结构中构建管道的教程:

http://www.cse.ohio-state.edu/~mamrak/CIS762/pipes_lab_notes.html

没有太多完整的代码,但它很好地描述了这些概念。

你也可以下载任何shell的源代码,例如bash , tcsh , zsh等。

试着阅读Bash的源代码,看看他们是如何做到的。

几年前,当我需要做类似的shell时,我使用了“ 实用Unix编程 ”一书。

这对许多IPC主题的例子非常有用。 我的办公桌上还有一个副本,我经常参考。 对于$ 2 – $ 9使用,这是一个相当不错的价值,你会得到什么。

为了什么它值得,只是想我会提到它。

下面是我上一学期系统编程课的钢管笔记。

您将每个程序的输入连接到自己的输出。 您可能希望将每个程序的输出连接到下一个输入。

而不是在一个管道中的n个进程的一般情况,你应该从两个基础开始,并从那里扩大。 如果继续扩展工作代码而不是直接为复杂结构进行拍摄,您将更好地理解文件描述符之间的插入方式。

好吧,我没有答案,但我正在处理同样的问题atm。 我会分享我的。 它适用于这两个命令,但它运行后,I / O被打破。 以一种奇怪的方式,我还没有弄清楚。 打电话给水管工!

 void pipeCommand(char** cmd1, char** cmd2) { int fds[2]; // file descriptors pipe(fds); int oldIn, oldOut; // child process #1 if (fork() == 0) { // Reassign stdin to fds[0] end of pipe. oldIn = dup(STDIN_FILENO); dup2(fds[0], STDIN_FILENO); close(fds[1]); close(fds[0]); // Execute the second command. execvp(cmd2[0], cmd2); // child process #2 } else if ((fork()) == 0) { oldOut = dup(STDOUT_FILENO); // Reassign stdout to fds[1] end of pipe. dup2(fds[1], STDOUT_FILENO); close(fds[0]); close(fds[1]); // Execute the first command. execvp(cmd1[0], cmd1); // parent process } else wait(NULL); dup2(oldIn, STDIN_FILENO); dup2(oldOut, STDOUT_FILENO); close(oldOut); close(oldIn); } 

我有一种感觉,就是在等待之后,我们该做什么或者不该做什么()