【发布时间】:2011-07-04 15:44:59
【问题描述】:
我已经阅读了大约 50 篇关于这个主题的帖子和教程,我已经复制、编写和测试了大约 20 种替代方案,并完成了我能想到的所有可能的研究。不过,我还没有看到以下问题的有效解决方案:
父进程A想要将数据传递给外部进程B,让进程B修改数据并将其传递回父进程A,然后继续父进程A。进程B是我拥有的外部程序套件的一部分没有影响,通常在 UNIX 命令行上这样运行:
< input_data program_B1 | program_B2 | program_B3 > output_data
...在哪里
input_data, output_data:在程序B1-B3中处理的一些数据
program_B1,B2,B3:从 stdin (fread) 读取数据并输出到 stdout (fwrite) 并对数据进行一些处理的程序。
所以,按顺序:
(1)父进程A向子进程B传递数据
(2)子进程B读取数据并修改
(3)子进程B将数据传回父进程A
(4) 父进程 A 读取数据并继续(例如将其进一步传递给进程 B2..)。
(5)父进程A将另一个数据集传递给子进程B等
问题是,无论我做什么,程序几乎总是挂在对管道的读/读(或写/写?)上。
需要注意的重要一点是,父进程在将数据传递给子进程后不能简单地关闭管道,因为它在循环中工作,并且希望在完成处理后将另一组数据传递给子进程第一组。
这是一组父/子程序(使用 g++ pipe_parent.cc -o pipe_parent、g++ pipe_child.cc -o pipe_child 编译)说明未命名管道的问题。我也尝试过命名管道,但没有那么广泛。每次执行的结果可能略有不同。如果 sleep 语句在父级中省略,或 fflush() 语句在子级中省略,管道几乎肯定会阻塞。如果要传递的数据量增加,它总是会阻塞,独立于 sleep 或 fflush。
父程序 A:
#include <cstring>
#include <cstdio>
#include <cstdlib>
extern "C" {
#include <unistd.h>
#include <fcntl.h>
}
using namespace std;
/*
* Parent-child inter-communication
* Child is external process
*/
int main() {
int fd[2];
if( pipe(fd) == -1 ) {
fprintf(stderr,"Unable to create pipe\n");
}
int fd_parentWrite = fd[1];
int fd_childRead = fd[0];
if( pipe(fd) == -1 ) {
fprintf(stderr,"Unable to create pipe\n");
exit(-1);
}
int fd_childWrite = fd[1];
int fd_parentRead = fd[0];
pid_t pid = fork();
if( pid == -1 ) {
fprintf(stderr,"Unable to fork new process\n");
exit(-1);
}
if( pid == 0 ) { // Child process
dup2( fd_childRead, fileno(stdin) ); // Redirect standard input(0) to child 'read pipe'
dup2( fd_childWrite, fileno(stdout) ); // Redirect standard output(1) to child 'write pipe'
close(fd_parentRead);
close(fd_parentWrite);
close(fd_childRead);
close(fd_childWrite);
// execl replaces child process with an external one
int ret = execl("/disk/sources/pipe_test/pipe_child","pipe_child",NULL);
fprintf(stderr,"External process failed, return code: %d...\n", ret);
exit(-1);
// Child process is done. Will not continue from here on
}
else { // Parent process
// Nothing to set up
}
// ...more code...
if( pid > 0 ) { // Parent process (redundant if statement)
int numElements = 10000;
int totalSize = numElements * sizeof(float);
float* buffer = new float[numElements];
for( int i = 0; i < numElements; i++ ) {
buffer[i] = (float)i;
}
for( int iter = 0; iter < 5; iter++ ) {
fprintf(stderr,"--------- Iteration #%d -----------\n", iter);
int sizeWrite = (int)write( fd_parentWrite, buffer, totalSize );
if( sizeWrite == -1 ) {
fprintf(stderr,"Parent process write error\n");
exit(-1);
}
fprintf(stderr,"Parent #%d: Wrote %d elements. Total size: %d\n", iter, sizeWrite, totalSize);
sleep(1); // <--- CHANGE!
int sizeRead = (int)read( fd_parentRead, buffer, totalSize );
if( sizeRead <= 0 ) {
fprintf(stderr,"Parent process read error\n");
}
while( sizeRead < totalSize ) {
fprintf(stderr,"Parent #%d: Read %d elements, continue reading...\n", iter, sizeRead);
int sizeNew = (int)read( fd_parentRead, &buffer[sizeRead], totalSize-sizeRead );
fprintf(stderr," ...newly read %d elements\n", sizeNew);
if( sizeNew < 0 ) {
exit(-1);
}
sizeRead += sizeNew;
}
fprintf(stderr,"Parent #%d: Read %d elements. Total size: %d\n", iter, sizeRead, totalSize);
fprintf(stderr,"Examples : %f %f %f\n", buffer[0], buffer[10], buffer[100]);
}
delete [] buffer;
}
close(fd_parentRead);
close(fd_parentWrite);
close(fd_childRead);
close(fd_childWrite);
return 0;
}
儿童计划 B:
#include <cstdio>
using namespace std;
int main() {
int numElements = 10000;
int totalSize = numElements * sizeof(float);
float* buffer = new float[numElements];
int counter = 0;
int sizeRead = 0;
do {
sizeRead = fread( buffer, 1, totalSize, stdin);
fprintf(stderr,"Child #%d: Read %d elements, buffer100: %f\n", counter, sizeRead, buffer[100]);
if( sizeRead > 0 ) {
for( int i = 0; i < numElements; i++ ) {
buffer[i] += numElements;
}
int sizeWrite = fwrite( buffer, 1, totalSize, stdout);
fflush(stdout); // <--- CHANGE!
fprintf(stderr,"Child #%d: Wrote %d elements\n", counter, sizeWrite);
counter += 1;
}
} while( sizeRead > 0 );
return 0;
}
有什么方法可以检查管道何时有足够的数据可供读取?或者有没有其他方法可以解决上述问题,有或没有管道?
请帮忙!
【问题讨论】:
-
您确定
process B(您无法控制的那个)支持这种操作模式吗?许多程序的编写假设它应该在标准输入关闭之前读取,同时缓冲输出(通过写入缓冲的 FILE* 而不是 fflush 来显式或隐式地写入)。这很容易导致死锁,例如您看到的死锁,因为在程序终止/标准输入关闭之前,程序不会输出其最后的数据块。 -
@nos,我相信这对我来说应该不是问题,除非 fwrite 到 stdout 被系统缓冲。我可以看到
program B的源代码,它所做的只是使用来自标准输入的 fread 和 fwrite 到标准输出。但它不会刷新,我无法对代码进行此类更新。 -
fwrite 到 stdout 通常是缓冲的,在大多数系统上,您必须明确关闭缓冲(或确保在适当的时间调用 fflush())
标签: process fork pipe external