经验法则:如果您使用dup()
或dup2()
将管道的一端映射到标准输入或标准输出,则应该close()
使用管道本身的两端。你没有那样做;您的等待正在等待程序完成,但程序不会完成,因为仍然有一个管道打开的过程可以写入管道。此外,创建管道的进程需要关闭管道的两端,因为它本身不使用管道(子进程正在使用它)。另请参阅C MiniShell — 添加管道。
此外,您不应该等待第一个孩子完成后再启动第二个孩子(所以这pid2 = wait(&status2);
条线是个坏主意)。管道的容量相当小;如果要传输的总数据太大,写子可能会阻塞等待读子读取,但是读子还没有开始,因为它正在等待写子退出(并且需要很长时间让这个僵局自行解决)。您会看到输出没有wait()
调用,因为管道的第二部分执行并处理来自管道第一部分的数据,但它仍在等待来自 shell 的更多数据。
考虑到这些提示,您最终可能会得到:
pipe(mypipe);
pid1 = fork();
if (pid1 == 0)
{
pid2 = fork();
if (pid2 == 0)
{
close(0);
dup(mypipe[0]);
close(mypipe[1]);
close(mypipe[0]);
execv(foundnode2->path_dir, arv2);
fprintf(stderr, "Failed to exec %s\n", foundnode2->path_dir);
exit(1);
}
close(1);
dup(mypipe[1]);
close(mypipe[0]);
close(mypipe[1]);
execv(foundnode1->path_dir, arv1);
fprintf(stderr, "Failed to exec %s\n", foundnode1->path_dir);
exit(1);
}
close(mypipe[0]);
close(mypipe[1]);
pid1 = wait(&status1);
注意命令失败时向标准错误报告的错误execv()
。此外,退出状态 0 应保留为成功;1 是一个方便的错误退出状态,或者您可以使用EXIT_FAILURE
from <stdlib.h>
。
仍然省略了很多错误检查;操作可能会fork()
失败;可能会pipe()
失败。一个后果是,如果第二个fork()
失败,您仍然会启动第二个孩子(由 标识foundnode1->path_dir
)。
而且我注意到,您可以通过将管道创建移动到第一个子进程中来节省一些工作(然后父进程不需要 - 实际上,不能 - 关闭管道):
int pid1 = fork();
if (pid1 == 0)
{
int mypipe[2];
pipe(mypipe);
int pid2 = fork();
if (pid2 == 0)
{
close(0);
dup(mypipe[0]);
close(mypipe[1]);
close(mypipe[0]);
execv(foundnode2->path_dir, arv2);
fprintf(stderr, "Failed to exec %s\n", foundnode2->path_dir);
exit(1);
}
close(1);
dup(mypipe[1]);
close(mypipe[0]);
close(mypipe[1]);
execv(foundnode1->path_dir, arv1);
fprintf(stderr, "Failed to exec %s\n", foundnode1->path_dir);
exit(1);
}
pid1 = wait(&status1);