进程间通信,从多个 children stdout 读取
Interprocess communication, reading from multiple children stdout
我正在尝试编写自定义 shell-like 程序,其中可以同时执行多个命令。对于单个命令,这并不复杂。但是,当我尝试同时执行多个命令(每个命令在一个单独的 child 中)并捕获它们的标准输出时,我遇到了问题。
到目前为止我尝试的是在我的 shell 应用程序下我有两个函数可以同时 运行 命令,execute()
接受多个命令并且对于每个命令它 fork()
一个child进程来执行命令,subprocess()
需要1个cmd并执行它。
void execute(std::vector<std::string> cmds) {
int fds[2];
pipe(fds);
std::pair<pid_t, int> sp;
for (int i = 0; i < cmds.size(); i++) {
std::pair<pid_t, int> sp = this->subprocess(cmds[i], fds);
}
// wait for all children
while (wait(NULL) > 0);
close(sp.second);
}
std::pair<pid_t, int> subprocess(std::string &cmd, int *fds) {
std::pair<pid_t, int> process = std::make_pair(fork(), fds[0]);
if (process.first == 0) {
close(fds[0]); // no reading
dup2(fds[1], STDIN_FILENO);
close(fds[1]);
char *argv[] = {"/bin/sh", "-c", cmd.data(), NULL};
execvp(argv[0], argv);
exit(0);
}
close(fds[1]); // only reading
return process;
}
这里的问题是,当我在我的自定义 shell 上执行多个命令时(这里不深入研究细节,但它会在某些时候调用 execute()
。)如果我使用 STDIN_FILENO
如上捕获 child 进程 stdout,它会永远写入 shell 的 stdin 捕获的输出是什么,例如
如果输入命令是
echo im done, yet?
echo nope
echo maybe
然后,在写入 STDIN_FILENO
的情况下,输出就像(其中 >>>
)是我的用户输入标记。
im done, yet?
nope
maybe
>>> nope
maybe
im done, yet?
>>> im done, yet?
nope
maybe
在写给STDOUT_FILENO
的情况下,似乎忽略了其中一个命令(可能是第一个child),我不知道为什么?
maybe
nope
>>> maybe
nope
>>> nope
maybe
>>> maybe
nope
>>> nope
所以,我认为潜在的东西在我的 shell 中,我在 while 循环中使用 std::cin >> ...
进行用户输入,这可能与标准输入案例有某种冲突。另一方面,在主进程中(parent)我正在等待所有 children 退出,所以 children 不知何故没有退出,但 child 应该在 execvp 之后死掉,对吗?而且,我在主进程close(sp.second)
关闭了读取端。在这一点上,我不确定为什么会发生这种情况?
我不应该对这样的过程使用 pipe()
吗?如果我使用临时文件重定向 child 进程的标准输出,一切都会好起来吗?如果是这样,你能解释一下为什么吗?
显示的代码中存在多个基本的概念性问题。
std::pair<pid_t, int> sp;
这声明了一个新的 std::pair
object。到目前为止一切顺利。
std::pair<pid_t, int> sp = this->subprocess(cmds[i], fds);
这会在 for
循环中声明一个新的 std::pair
object。它恰好与函数范围内的 sp
object 同名。但它是一个不同的 object,与它毫无关系。这就是 C++ 的工作方式:当您在内部作用域、if
语句、for
循环或另一对 {
中的任何内容中声明 object 时。 .. }
你最终声明了一个新的 object。它的名称是否恰好与在更大范围内声明的另一个名称相同,无关紧要。这是一个新的 object.
// wait for all children
while (wait(NULL) > 0);
close(sp.second);
这里有两个不同的问题。
对于初学者来说,如果我们一直注意的话:这个 sp
object 还没有被初始化为任何东西。
如果这里的目标是从children读取,那部分就完全没有了,应该在之前 等待 child 进程退出。如果,正如此处描述的目标,child 进程将写入此管道,则应从中读取管道。否则,如果没有从管道中读取任何内容:管道的内部缓冲区是有限的,如果 child 进程填满了管道,它们将被阻塞,等待管道被读取。但是 parent 进程正在等待 child 进程存在,所以一切都会挂起。
最后,还不清楚为什么管道的文件描述符被传递给同一个函数,只传递给 return 具有相同文件描述符的 std::pair
。 std::pair
在显示的代码中没有任何用处,因此很可能还有更多代码未在此处显示,而在此处使用它。
至少必须解决上述所有问题,才能使显示的代码正常工作。如果还有其他代码未显示,它也可能有也可能没有其他问题。
我正在尝试编写自定义 shell-like 程序,其中可以同时执行多个命令。对于单个命令,这并不复杂。但是,当我尝试同时执行多个命令(每个命令在一个单独的 child 中)并捕获它们的标准输出时,我遇到了问题。
到目前为止我尝试的是在我的 shell 应用程序下我有两个函数可以同时 运行 命令,execute()
接受多个命令并且对于每个命令它 fork()
一个child进程来执行命令,subprocess()
需要1个cmd并执行它。
void execute(std::vector<std::string> cmds) {
int fds[2];
pipe(fds);
std::pair<pid_t, int> sp;
for (int i = 0; i < cmds.size(); i++) {
std::pair<pid_t, int> sp = this->subprocess(cmds[i], fds);
}
// wait for all children
while (wait(NULL) > 0);
close(sp.second);
}
std::pair<pid_t, int> subprocess(std::string &cmd, int *fds) {
std::pair<pid_t, int> process = std::make_pair(fork(), fds[0]);
if (process.first == 0) {
close(fds[0]); // no reading
dup2(fds[1], STDIN_FILENO);
close(fds[1]);
char *argv[] = {"/bin/sh", "-c", cmd.data(), NULL};
execvp(argv[0], argv);
exit(0);
}
close(fds[1]); // only reading
return process;
}
这里的问题是,当我在我的自定义 shell 上执行多个命令时(这里不深入研究细节,但它会在某些时候调用 execute()
。)如果我使用 STDIN_FILENO
如上捕获 child 进程 stdout,它会永远写入 shell 的 stdin 捕获的输出是什么,例如
如果输入命令是
echo im done, yet?
echo nope
echo maybe
然后,在写入 STDIN_FILENO
的情况下,输出就像(其中 >>>
)是我的用户输入标记。
im done, yet?
nope
maybe
>>> nope
maybe
im done, yet?
>>> im done, yet?
nope
maybe
在写给STDOUT_FILENO
的情况下,似乎忽略了其中一个命令(可能是第一个child),我不知道为什么?
maybe
nope
>>> maybe
nope
>>> nope
maybe
>>> maybe
nope
>>> nope
所以,我认为潜在的东西在我的 shell 中,我在 while 循环中使用 std::cin >> ...
进行用户输入,这可能与标准输入案例有某种冲突。另一方面,在主进程中(parent)我正在等待所有 children 退出,所以 children 不知何故没有退出,但 child 应该在 execvp 之后死掉,对吗?而且,我在主进程close(sp.second)
关闭了读取端。在这一点上,我不确定为什么会发生这种情况?
我不应该对这样的过程使用 pipe()
吗?如果我使用临时文件重定向 child 进程的标准输出,一切都会好起来吗?如果是这样,你能解释一下为什么吗?
显示的代码中存在多个基本的概念性问题。
std::pair<pid_t, int> sp;
这声明了一个新的 std::pair
object。到目前为止一切顺利。
std::pair<pid_t, int> sp = this->subprocess(cmds[i], fds);
这会在 for
循环中声明一个新的 std::pair
object。它恰好与函数范围内的 sp
object 同名。但它是一个不同的 object,与它毫无关系。这就是 C++ 的工作方式:当您在内部作用域、if
语句、for
循环或另一对 {
中的任何内容中声明 object 时。 .. }
你最终声明了一个新的 object。它的名称是否恰好与在更大范围内声明的另一个名称相同,无关紧要。这是一个新的 object.
// wait for all children
while (wait(NULL) > 0);
close(sp.second);
这里有两个不同的问题。
对于初学者来说,如果我们一直注意的话:这个
sp
object 还没有被初始化为任何东西。如果这里的目标是从children读取,那部分就完全没有了,应该在之前 等待 child 进程退出。如果,正如此处描述的目标,child 进程将写入此管道,则应从中读取管道。否则,如果没有从管道中读取任何内容:管道的内部缓冲区是有限的,如果 child 进程填满了管道,它们将被阻塞,等待管道被读取。但是 parent 进程正在等待 child 进程存在,所以一切都会挂起。
最后,还不清楚为什么管道的文件描述符被传递给同一个函数,只传递给 return 具有相同文件描述符的 std::pair
。 std::pair
在显示的代码中没有任何用处,因此很可能还有更多代码未在此处显示,而在此处使用它。
至少必须解决上述所有问题,才能使显示的代码正常工作。如果还有其他代码未显示,它也可能有也可能没有其他问题。