fork/pipe/dup/execvp
概念,但遇到了一个小问题:我做了一个测试 main 来执行排序并通过绑定到 STDIN 的管道手动发送一些数字,它按预期工作:当我关闭 stdin 编写器时,我可以读取排序后的数字输出。
但是,如果我也用他自己的一对管道(未连接到排序中使用的管道)以相同的方式启动 sed(或侦听 STDIN 的其他程序),那么我会从排序管道中读取一个块,即如果我只评论 sed 执行部分就消失了。
这个想法是能够从“第一个”读取并让其他人等待命令发送。
所以问题是: a) 它们是否共享相同的 STDIN,因此当 sed 启动时,它会被视为可能的写入者,这就是读取块的原因? 和 b) 那么我如何才能从同一个父进程
生成这 N 个进程,使它们的 STDIN 彼此不依赖? 这是测试代码:
#include <iostream>
#include <unistd.h>
#include <wait.h>
int sed_pipe_in[2], sed_pipe_out[2], sort_pipe_in[2], sort_pipe_out[2];
pid_t execute_sort() {
pid_t sort = fork();
if (sort == 0) {
close(sort_pipe_in[1]);
dup2(sort_pipe_in[0], STDIN_FILENO);
close(sort_pipe_in[0]);
close(sort_pipe_out[0]);
dup2(sort_pipe_out[1], STDOUT_FILENO);
close(sort_pipe_out[1]);
char* args[] = { "sort", "-", NULL };
execvp("/usr/bin/sort", args);
std::cerr << "Execvp failed!" << std::endl;
exit(-1);
}
else if (sort == -1) {
std::cerr << "Fork failed!" << std::endl;
exit(-1);
}
else {
close(sort_pipe_in[0]);
close(sort_pipe_out[1]);
}
return sort;
}
pid_t execute_sed() {
pid_t sed = fork();
if (sed == 0) {
close(sed_pipe_in[1]);
dup2(sed_pipe_in[0], STDIN_FILENO);
close(sed_pipe_in[0]);
close(sed_pipe_out[0]);
dup2(sed_pipe_out[1], STDOUT_FILENO);
close(sed_pipe_out[1]);
char* args[] = { "sed", "-e", "s/3/9/", NULL };
execvp("/bin/sed", args);
std::cerr << "Execvp failed!" << std::endl;
exit(-1);
}
else if (sed == -1) {
std::cerr << "Fork failed!" << std::endl;
exit(-1);
}
else {
close(sed_pipe_in[0]);
close(sed_pipe_out[1]);
}
return sed;
}
int main() {
pipe(sed_pipe_in);
pipe(sed_pipe_out);
pipe(sort_pipe_in);
pipe(sort_pipe_out);
pid_t sort = execute_sort();
// Why removing the following line makes the program to work
// if sed is not yet connected to sort?
pid_t sed = execute_sed();
// Only parent will get here
// Sending input to sort
write(sort_pipe_in[1], "3\n2\n1\n", 7);
close(sort_pipe_in[1]);
char buffer[500];
// If sed is started then this read is blocking
ssize_t bytes = read(sort_pipe_out[0], buffer, 500);
std::string output(buffer, bytes);
std::cout << "Output is " << output << std::endl;
// Sort will exit here as stdin is closed
int status;
waitpid(sort, &status, 0);
return 0;
}
如果
execute_sed
被注释掉,那么输出是正确的:
1
2
3
但是如果
execute_sed
没有被注释,那么该进程在读取时被阻止。
sort
和
sed
分叉,另一个分叉将保持另一个进程管道的文件描述符打开(因为分叉的子进程继承文件描述符)。由于 sorts
的
STDIN
仍然打开(并由 sed
进程持有),因此它继续等待输入。您可以只拥有每个不需要的文件描述符,使用 close()
创建管道(这将使它们在
pipe2(res_pipe, O_CLOEXEC)
上自动关闭),或者将 exec*
与 fcntl
/F_GETFD
一起使用使用 F_SETFD
标记管道文件描述符以获得相同的结果。