簡體   English   中英

select()之后的read()在從生成的進程中從管道讀取時阻塞

[英]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等)。


[1]請參見Linux手冊頁BUGS部分

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM