有3个管道调用来为新进程创建stdin,stdout,stderr。调用fork(),调用exec()。这包装在一个有效的popen2函数中。
使用此popen2函数时,即使在select()返回已准备好读取的读取之后,也从read()上的新处理块中从stdout读取。我希望它能够读取()。我唯一的猜测是此fd的read()试图填充输入buf。
一个例外:如果关闭了stdin,则子进程将关闭stdout,即使无法填充buf也会完成读取。
我需要的是read()返回准备读取的内容?默认模式是否已缓冲,我不知道吗?
pid_t popen2(const char *const argv[], int *in, int *out, int *err)
{
int res;
pid_t pid = 0;
int inpipefd[2];
int outpipefd[2];
int errpipefd[2];
if(0!=pipe(inpipefd)) {
perror("allocating pipe for child stdin");
return -1;
}
if(0!=pipe(outpipefd)) {
close(inpipefd[0]);
close(inpipefd[1]);
perror("allocating pipe for child stdout");
return -1;
}
if(0!=pipe(errpipefd)) {
close(inpipefd[0]);
close(inpipefd[1]);
close(outpipefd[0]);
close(outpipefd[1]);
perror("allocating pipe for child stderr");
return -1;
}
pid = fork();
if (0==pid) {
if (-1==dup2(inpipefd[0], STDIN_FILENO)) {exit(errno);}
if (-1==dup2(outpipefd[1], STDOUT_FILENO)) {exit(errno);}
if (-1==dup2(errpipefd[1], STDERR_FILENO)) {exit(errno);}
close(inpipefd[0]);
close(inpipefd[1]);
close(outpipefd[0]);
close(outpipefd[1]);
close(errpipefd[0]);
close(errpipefd[1]);
execvp(argv[0], (char* const*)argv);
perror("exec failed");
exit(1);
}
close(inpipefd[0]);
close(outpipefd[1]);
close(errpipefd[1]);
*in = inpipefd[1];
*out = outpipefd[0];
*err = errpipefd[0];
return pid;
}
...
if(0>=(pid = popen2(argv, &in, &out, &err))) {
return make_unique<std::string>();
}
res = writeall(in, cont.c_str(), cont.length());
if(res==-1) {
goto err;
}
close(in);
...
unique_ptr<std::string> DecryptProcess::Read() {
auto result = make_unique<std::string>();
const unsigned int BUFLEN = 1024*16;
std::vector<char> buf(BUFLEN);
fd_set rfds;
struct timeval tv;
int n;
int fcnt;
FD_ZERO(&rfds);
FD_SET(out_fd, &rfds);
FD_SET(err_fd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 100000;
fcnt = select(std::max(out_fd, err_fd)+1, &rfds, NULL, NULL, &tv);
if (fcnt == -1) {
return result;
} else if (!fcnt) {
return result;
}
if (FD_ISSET(err_fd, &rfds)) {
n = read(err_fd, &buf[0], buf.size());
}
if (FD_ISSET(out_fd, &rfds)) {
do
{
n = read(out_fd, &buf[0], buf.size());
if (n == -1) {
return result;
}
if (n>0)
result->append(buf.cbegin(), buf.cbegin()+n);
} while ( n > 0 );
}
return result;
}
答案 0 :(得分:3)
[删除了只会使视图混乱的调试语句]
if (FD_ISSET(out_fd, &rfds)) {
do
{
n = read(out_fd, &buf[0], buf.size());
if (n == -1) {
return result;
}
if (n>0)
result->append(buf.cbegin(), buf.cbegin()+n);
} while ( n > 0 );
}
这里您不是在做一个read()
,而是在循环中做read()
,直到它返回错误或命中EOF为止。因此,代码将循环运行,直到将消耗完已写入管道的所有数据,然后再阻塞,直到将更多数据写入管道为止。 select()
仅返回准备读取的文件,表明有一些数据可供读取,而不是read()
会在EOF之前阻塞。
顺便说一句,select()
不能保证,即使在标记为准备读取的fd上只有一个read()
也会不会实际被阻止。 [1] 唯一可以确保的方法是将fd设置为非阻塞模式(例如,使用fcntl(O_NONBLOCK)
),并在错误情况下检查errno == EAGAIN
您的代码还有许多其他问题(例如,对stdio缓冲区进行两次刷新,无法检查EINTR
等)。