分解沟通,从多个孩子那里阅读
我正在尝试编写一个自定义的类似 shell 的程序,其中可以同时执行多个命令。对于单个命令来说,这并不复杂。但是,当我尝试同时执行多个命令(每个命令都在一个单独的子命令中)并捕获它们的标准输出时,我遇到了问题。
到目前为止,我尝试的是在我的 shell 应用程序下,我有两个函数可以同时运行命令,execute()
接受多个命令,并且对于每个命令,它fork()
执行命令的子进程,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
如上所述,捕获子进程标准输出,它会永远写入 shell 的标准输入,捕获的输出是什么,例如
,如果输入命令是
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
情况下,它似乎忽略了其中一个命令(可能是第一个子命令),我不确定为什么?
maybe
nope
>>> maybe
nope
>>> nope
maybe
>>> maybe
nope
>>> nope
因此,我认为潜在的事情就在我正在使用 std::cin >> 的 shell 中。 ... 对于 while 循环中的用户输入,这可能会与 stdin 大小写发生冲突。另一方面,在主进程(父进程)中,我正在等待所有子进程退出,因此子进程不知何故没有退出,但子进程应该在 execvp 之后死亡,对吧?而且,我在主进程close(sp.second)
中关闭了读取端。此时,我不确定为什么会发生这种情况?
对于这样的过程,我不应该使用 pipe()
吗?如果我使用临时文件来重定向子进程的标准输出,一切都会好吗?如果是这样,您能解释一下原因吗?
I'm trying to write a custom shell-like program, where multiple commands can be executed concurrently. For a single command this is not much complicated. However, when I try to concurrently execute multiple commands (each one in a separate child) and capture their stdout I'm having a problem.
What I tried so far is this under my shell application I have two functions to run the commands concurrently, execute()
takes multiple commands and for each of the commands it fork()
a child process to execute the command, subprocess()
takes 1 cmd and executes it.
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;
}
The problem here is, when I execute multiple commands on my custom shell (not diving into spesifics here, but it will call execute()
at some point.) if I use STDIN_FILENO
as above to capture child process stdout, it keeps writing to shell's stdin forever what the captured output is, for example
if the input commands are
echo im done, yet?
echo nope
echo maybe
then, in writing to STDIN_FILENO
case, the output is like (where >>>
) is my marker for user input.
im done, yet?
nope
maybe
>>> nope
maybe
im done, yet?
>>> im done, yet?
nope
maybe
in writing to STDOUT_FILENO
case, it seems it's ignoring one of the commands (probably the first child), I'm not sure why?
maybe
nope
>>> maybe
nope
>>> nope
maybe
>>> maybe
nope
>>> nope
So, potential things I thought are in my shell I'm using std::cin >> ...
for user input in a while loop ofc, this may somehow conflict with stdin case. On the other hand, in the main process (parent) I'm waiting for all children to exit, so children somehow is not exiting, but child should die off after execvp, right ? Moreover, I close the reading end in the main process close(sp.second)
. At this point, I'm not sure why this case happens ?
Should I not use pipe()
for a process like this ? If I use a temp file to redirect stdout of child process, would everything be fine ? and if so, can you please explain why ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
所示代码中存在多个基本的概念问题。
这声明了一个新的 std::pair 对象。到目前为止,一切都很好。
这在
for
循环内声明了一个新的std::pair
对象。它恰好与函数作用域中的sp
对象具有相同的名称。但它是一个不同的对象,与它无关。这就是 C++ 的工作原理:当您在内部作用域、if
语句、for
循环或填充在另一对{ 中的任何内容中声明对象时,
...}
你最终声明了一个新对象。它的名称是否恰好与在更大范围内声明的另一个名称相同并不重要。这是一个新对象。这里有两个独立的问题。
对于初学者来说,如果我们一直注意的话:这个
sp
对象尚未初始化为任何内容。如果这里的目标是从子进程中读取数据,则该部分完全丢失,并且应该在等待子进程退出之前完成。正如此处所描述的目标,如果子进程将写入该管道,则应从中读取该管道。否则,如果没有从管道中读取任何内容:管道的内部缓冲区是有限的,如果子进程填满管道,它们将被阻塞,等待从管道中读取数据。但是父进程正在等待子进程存在,所以一切都会挂起。
最后,还不清楚为什么管道的文件描述符被传递给相同的函数,只是为了返回具有相同文件描述符的 std::pair 。
std::pair
在显示的代码中没有任何有用的用途,因此很可能还有更多此处未显示的代码,其中使用了它。至少必须解决上述所有问题才能使所示代码正常工作。如果还有其他代码未显示,则它也可能存在也可能不存在其他问题。
There are multiple, fundamental, conceptual problems in the shown code.
This declares a new
std::pair
object. So far so good.This declares a new
std::pair
object inside thefor
loop. It just happens to have the same name as thesp
object at the function scope. But it's a different object that has nothing to do, whatsoever, with it. That's how C++ works: when you declare an object inside an inner scope, inside anif
statement, afor
loop, or anything that's stuffed inside another pair of{
...}
you end up declaring a new object. Whether its name happens to be the same as another name that's been declared in a larger scope, it's immaterial. It's a new object.There are two separate problems here.
For starters, if we've been paying attention: this
sp
object has not been initialized to anything.If the goal here is to read from the children, that part is completely missing, and that should be done before waiting for the child processes to exit. If, as the described goal is here, the child processes are going to be writing to this pipe the pipe should be read from. Otherwise if nothing is being read from the pipe: the pipe's internal buffer is limited, and if the child processes fill up the pipe they'll be blocked, waiting for the pipe to be read from. But the parent process is waiting for the child processes to exist, so everything will hang.
Finally, it is also unclear why the pipe's file descriptor is getting passed to the same function, only to return a
std::pair
with the same file descriptor. Thestd::pair
serves no useful purpose in the shown code, so it's likely that there's also more code that's not shown here, where this is put to use.At least all of the above problems must be fixed in order for the shown code to work correctly. If there's other code that's not shown, it may or may not have additional issues, as well.