read () после блокировки select () при чтении из канала из порожденного процесса - PullRequest
0 голосов
/ 10 мая 2019

Существует 3 конвейерных вызова для создания stdin, stdout, stderr для нового процесса.вызывается fork (), вызывается exec ().Это обернуто в функцию popen2, которая работает.

При использовании этой функции popen2 чтение из stdout из новых блоков процесса в read (), даже после того, как select () возвращает, что read готов для чтения.Я ожидаю, что он сможет читать ().Мое единственное предположение, что read () для этого fd пытается заполнить входной буфер.

Одно исключение: если 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;
    }


1 Ответ

3 голосов
/ 10 мая 2019

[удалили операторы отладки, которые только загромождают представление]

    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() не дает никаких гарантий , что даже один read() на fd, помеченном как готовый к чтению, не фактически блокирует. [1] Единственный способубедитесь, что это для установки fd в неблокирующем режиме (например, с fcntl(O_NONBLOCK)) и проверьте для errno == EAGAIN в случае ошибки.

Ваш код имеет много других проблем (например, двойная очисткабуферов stdio, ошибка проверки EINTR и т. д.)


[1] См. раздел BUGS справочной страницы Linux .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...