[英]read() after select() blocking when reading from pipe from spawned process
有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;
}
[刪除了只會使視圖混亂的調試語句]
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()
將文件返回為只讀狀態僅表明有一些數據可從文件中讀取,而不是直到EOF才會阻塞read()
。
順便說一句, select()
無法保證即使標記為准備讀取的fd上的單個read()
也不會實際阻塞。 [1]確保這一點的唯一方法是將fd設置為非阻塞模式(例如,使用fcntl(O_NONBLOCK)
),並在錯誤情況下檢查errno == EAGAIN
。
您的代碼還有許多其他問題(例如,對stdio緩沖區進行兩次刷新,無法檢查EINTR
等)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.