我正在用C写一个shell,并试图实现多个管道。我已经通过创建一个二维数组来实现这个目标,该数组带有管道,并且每次都使用一个单独的管道。管道之间的所有命令都被一个解析函数分隔开,并放入一个结构体中。管道之间的每个命令行都有自己的进程。对于中间的所有命令,我都使用“我正试着从上一个进程读取数据并写入下一个进程。2问题从这里开始。3它对一个管道工作正常,然而,当我尝试多个管道时,我没有得到任何输出,程序卡住了。在GDB中,我在分叉第二个进程后从execvp得到一个失败的消息。这可能是由于什么原因?
int create_pipe(int* fd)
{
int pipe_id = pipe(fd);
if (pipe_id == -1)
{
return -1;
}
return 0;
}
void write_pipe(int* fd)
{
close(fd[READ]);
if ((dup2(fd[WRITE], STDOUT_FILENO)) < -1)
{
fork_error();
}
close(fd[WRITE]);
}
void read_pipe(int *fd)
{
close(fd[WRITE]);
if (dup2(fd[READ], STDIN_FILENO) < 0)
{
fork_error();
}
close(fd[READ]);
}
void need_to_pipe (int i, int (*fd)[2])
{
if (commands[i].pos == first)
{
write_pipe(fd[i * 2]);
}
else if (commands[i].pos == last)
{
read_pipe(fd[(i-1) *2]);
}
else //if (commands[i].pos == middle)
{
dup2(fd[(i-1)*2][READ], STDIN_FILENO);
close(fd[(i-1)*2][READ]);
close(fd[(i-1)*2][WRITE]);
//close(fd[(i)*2][READ]);
//close(fd[(i)*2][WRITE]);
close(fd[(i)*2][READ]);
dup2(fd[i*2][WRITE], STDOUT_FILENO);
close(fd[(i)*2][WRITE]);
}
}
/**
* Fork a proccess for command with index i in the command pipeline. If needed,
* create a new pipe and update the in and out members for the command..
*/
void fork_cmd(int i, int (*fd)[2]) {
pid_t pid;
switch (pid = fork()) {
case -1:
fork_error();
case 0:
// Child process after a successful fork().
if (!(commands[i].pos == single))
{
need_to_pipe(i, fd);
}
// Execute the command in the contex of the child process.
if (execvp(commands[i].argv[0], commands[i].argv)<0)
{
fprintf(stderr, "command not found: %s\n",
commands[i].argv[0]);
exit(EXIT_FAILURE);
}
default:
// Parent process after a successful fork().
break;
}
}
/**
* Fork one child process for each command in the command pipeline.
*/
void fork_cmds(int n, int (*fd)[2])
{
for (int i = 0; i < n; i++)
{
fork_cmd(i, fd);
}
}
void wait_once ()
{
wait(NULL);
}
/**
* Make the parents wait for all the child processes.
*/
void wait_for_all_cmds(int n)
{
for (int i = 0; i < n; i++)
{
wait_once();
//wait for number of child processes.
}
}
int main() {
int n; // Number of commands in a command pipeline.
size_t size = 128; // Max size of a command line string.
char line[size];
while(true) {
// Buffer for a command line string.
printf(" >>> ");
get_line(line, size);
n = parse_cmds(line, commands);
int fd[(n-1)][2];
for(int i =0;i<n-1;i++)
{
int pipe_id = pipe(fd[i*2]);
if (pipe_id == -1)
{
return -1;
}
}
fork_cmds(n, fd);
for(int i =0;i<n-1;i++)
{
int *fdclose= fd[i*2];
close (fdclose[READ]);
close (fdclose[WRITE]);
}
wait_for_all_cmds(n);
}
exit(EXIT_SUCCESS);
}
1条答案
按热度按时间zpjtge221#
您[可能]有太多的进程保持管道末端打开(它们 * 不 * 属于给定的子进程),因为您的循环在 * 任何分叉之前 * 打开了 * 所有 * 管道。
这给每个子进程带来了不必要的负担,因为它必须关闭 * 许多 * 管道末端,以防止它保持管道末端打开,从而防止其他子进程在其输入管道上看到EOF。
要了解这一点,为了在当前代码中进行调试,子程序可以执行以下操作(就在
exec*
调用之前)(例如):每个子节点在
stdin
、stdout
和stderr
上只能有 * 三个 * 开放流(例如0、1、2)。我想你会发现,有许多无关的/有害的流开放的各种儿童。
您只需要 * 两个 * 管道阵列(例如):
int pipeinp[2]; int pipeout[2];
最初,pipeinp
都是-1。大概...
父级应在
fork_cmd
的顶部[fork
之前]对pipeout
执行单个pipe
调用。子进程复制(并关闭)
pipeinp
的读取结束[如果不是-1]到stdin
。子进程复制/关闭
pipeout
到stdout
的写入端。它关闭
pipeout
的读取端。之后,父级应将
pipeout
拷贝到pipeinp
,并关闭pipeinp
的写入端所有管道级均应重复上述操作。
pipe
到pipeout
。并且,[last]子命令不应更改stdout
。举个例子,请看我的回答:fd leak, custom Shell