在过去的几天里,我一直在尝试编写自己的shell实现,但我似乎一直在努力让管道正常工作。我能够解析一条线并分开管道之间的命令(ex:ls | sort),但似乎无法将它们从一个管道输入到另一个管道。
我想我只是不明白如何正确使用dup2()和管道。
我现在已经包含了仍然失败的代码...... :(因此卡住了......
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);
}
}
答案 0 :(得分:10)
第一个建议:符号常数优于幻数。
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]
传递给第一个子流程,将dup2
传递给1,并传递{{1}到第二个子进程,它将fd[PIPE_READ]
改为0。
简单地看dup2
原型表明它无法做到:
forkAndExecute'
它只处理一个命令,并且从查看该参数列表开始,除非它转向邪恶的全局变量,否则它无法从其PrevCmd接收文件描述符或从其NextCmd接收文件描述符。
考虑如何管理所需的文件描述符,并重新设计void forkAndExecute( char* arrayOfWords[] , vector *vectorOfPIDs ,
bool hasNextCmd , bool hasPrevCmd);
以便能够使用这些描述符。
答案 1 :(得分:6)
一般过程会为此基本进程(伪代码)添加错误处理:
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()
首先创建管道。然后派生子进程,以便继承它。将文件描述符重新映射为0(stdin)和1(stdout),以便进程读取和写入适当的位置。关闭任何剩余的文件描述符,您不希望子进程在工作完成时查看或阻止。执行实际的子进程。等待他们完成,你就完成了!
答案 2 :(得分:6)
好这对我有用。希望这可以帮到你:
/************************
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);
}
答案 3 :(得分:5)
这是一个关于UNIX管道的教程,特别是关于如何在类似shell的架构中构建piplines:
http://www.cse.ohio-state.edu/~mamrak/CIS762/pipes_lab_notes.html
编写的代码不多,但它很好地描述了这些概念。
答案 4 :(得分:2)
尝试阅读source code of Bash,了解他们是如何做到的。
答案 5 :(得分:2)
几年前,当我需要做类似的shell时,我使用了这本书Practical Unix Programming。
对于许多IPC主题的示例非常有用。我的桌面上还有一份副本,我不时会参考。使用2美元 - 9美元,这对你得到的东西来说是一个非常好的价值。
对于它的价值,我想我会提到它。
答案 6 :(得分:1)
Here是我上一学期参加系统编程课程的管道笔记。
答案 7 :(得分:0)
您正在将每个程序的输入连接到自己的输出。您可能希望将每个程序的输出连接到下一个输入。
您应该从basis of two开始,然后从那里展开,而不是在管道中找到 n 进程的一般情况。如果继续扩展工作代码而不是直接针对复杂结构进行拍摄,您将更好地理解文件描述符相互插入的方式。
答案 8 :(得分:0)
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);
}
我有一种感觉,它与wait()
之后或不做的事情有关