0

我正在编写一个程序,它需要同时与外部程序进行双向通信,即同时读取和写入外部程序。

我创建了两个管道,一个用于向外部进程发送数据,一个用于从外部进程接收数据。在 fork 成为外部程序的子进程之后,父进程再次 fork。新的子进程现在将数据写入到外部程序的传出管道中,父进程现在从外部程序的传入管道中读取数据以进行进一步处理。

我听说使用 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));
        }
    }
}
4

2 回答 2

0

你需要小心这些事情。对做不同exit()事情,_exit()_Exit()与实际上正在处理,但它也可以作为直接调用来完成,具体取决于 C 库代码的编写方式]。_Exit_exitatexit()atexit()

您的大部分输出都是通过 完成的write,从应用程序的角度来看,它应该是无缓冲的。但你也在打电话cout << ...。您需要确保在退出之前已刷新。现在,您正在使用'\n'作为行尾标记,它可能会或可能不会刷新输出。如果将其更改为endl,它将刷新文件。现在您可以_Exit()从输出的角度安全地使用 - 例如,如果您的代码要设置自己的atexit()处理程序,打开临时文件或一堆其他类似的东西,这将是有问题的。如果你想在 fork 进程中做更复杂的事情,应该由另一个exec.

在你的程序中,没有任何待刷新的输出,所以无论如何它“工作”,但是如果你cout << ... << '\n';在代码的开头添加一个(或没有换行符)类型语句,你会看到它出错了. 如果添加cout.flush();,它将“修复”问题(基于您当前的代码)。

您还应该检查调用的返回值并在这种情况下execlp()调用_Exit()(并在主进程中处理它,以便在失败时不继续循环?)

于 2013-05-04T08:19:53.063 回答
0

在 fork() 的子分支中,使用 exit() 通常是不正确的,因为这会导致 stdio 缓冲区被刷新两次,并且临时文件被意外删除。在 C++ 代码中情况更糟,因为静态对象的析构函数可能运行不正确。(有一些不寻常的情况,比如守护进程,父进程应该调用 _exit() 而不是子进程;适用于绝大多数情况的基本规则是,对于 main 的每个入口,exit() 应该只调用一次.)

于 2017-03-25T20:05:10.280 回答