我正在编写一个程序,需要同时与外部程序进行双向通信,即同时读写外部程序。
我创建了两个管道,一个用于向外部进程发送数据,一个用于从外部进程接收数据。在分配成为外部程序的子进程之后,父进程再次分叉。新的子进程现在将数据写入到外部程序的传出管道中,父进程现在从外部程序中读取传入管道中的数据,以便进一步处理。
我听说使用exit(3)可能会导致缓冲区刷新两次,但是我也担心使用_exit(2)可能会使缓冲区保持未刷新状态。在我的程序中,分叉前后都有输出。在这种情况下,我应该使用exit(3)还是_exit(2)?
以下是我的主要功能。为简单起见,省略了#includes和辅助功能。
int main() {
srand(time(NULL));
ssize_t n;
cin >> n;
for (double p = 0.0; p <= 1.0; p += 0.1) {
string s = generate(n, p);
int out_fd[2];
int in_fd[2];
pipe(out_fd);
pipe(in_fd);
pid_t child = fork();
if (child) {
// parent
close(out_fd[0]);
close(in_fd[1]);
if (fork()) {
close(out_fd[1]);
ssize_t size = 0;
const ssize_t block_size = 1048576;
char buf[block_size];
ssize_t n_read;
while ((n_read = read(in_fd[0], buf, block_size)) != 0) {
size += n_read;
}
size += n_read;
close(in_fd[0]);
cout << "p = " << p << "; compress ratio = " << double(size) / double(n) << '\n'; // data written before forking (the loop continues to fork)
} else {
write(out_fd[1], s.data(), s.size()); // data written after forking
exit(EXIT_SUCCESS); // exit(3) or _exit(2) ?
}
} else {
// child
close(in_fd[0]);
close(out_fd[1]);
dup2(out_fd[0], STDIN_FILENO);
dup2(in_fd[1], STDOUT_FILENO);
close(STDERR_FILENO);
execlp("xz", "xz", "-9", "--format=raw", reinterpret_cast<char *>(NULL));
}
}
}
答案 0 :(得分:0)
你需要小心这些事情。 exit()
对_exit()
做了不同的事情,但又与_Exit()
做了不同的事情,并且正如duplicate所解释的答案所解释的那样_Exit
(与_exit
不同{1}},注意大写字母E)不会调用atexit()
个处理程序,或者刷新任何输出缓冲区,删除临时文件等[实际上可能是atexit()
处理,但它也可以完成作为直接调用,取决于C库代码的编写方式]。
您的大部分输出都是通过write
完成的,应该从应用程序的角度来看它是无缓冲的。但你也在呼叫cout << ...
。您需要确保在退出之前刷新它。现在,您使用'\n'
作为行结束标记,可能会也可能不会刷新输出。如果您将其更改为endl
,则会刷新文件。现在,您可以从输出角度安全地使用_Exit()
- 如果您的代码要设置自己的atexit()
处理程序,例如打开临时文件或其他一些此类内容,则会出现问题。如果您想在分叉流程中执行更复杂的操作,则应由另一个exec
完成。
在你的程序中,没有任何挂起的输出要刷新,所以无论如何它都“有效”,但如果你在开头添加cout << ... << '\n';
(或没有换行符)类型语句代码,你会发现它出错了。如果您添加cout.flush();
,它将“修复”问题(基于您当前的代码)。
您还应该检查execlp()
调用的返回值,并在这种情况下调用_Exit()
(并在主进程中处理它,以便在出现故障时不继续循环?)
答案 1 :(得分:0)
在fork()的子分支中,使用exit()通常是不正确的,因为这会导致stdio缓冲区被刷新两次,并且临时文件被意外删除。在C ++代码中情况更糟,因为静态对象的析构函数可能运行不正确。 (有一些不寻常的情况,比如守护进程,父进程应该调用_exit()而不是子进程;在绝大多数情况下适用的基本规则是,对于每个进入main的进入,应该只调用一次exit() 。)