Дочерний процесс заблокирован полным каналом, не может прочитать в родительском процессе - PullRequest
1 голос
/ 08 декабря 2011

Я примерно создал следующий код для вызова дочернего процесса:

// pipe meanings
const int READ = 0;
const int WRITE = 1;

int fd[2];
// Create pipes
if (pipe(fd))
  {
    throw ...
  }
p_pid = fork();
if (p_pid == 0) // in the child
  {
    close(fd[READ]);
    if (dup2(fd[WRITE], fileno(stdout)) == -1)
      {
        throw ...
      }
    close(fd[WRITE]);

    // Call exec
    execv(argv[0], const_cast<char*const*>(&argv[0]));
    _exit(-1);
  }
else if (p_pid < 0) // fork has failed
  {
    throw
  }
else // in th parent
  {
      close(fd[WRITE]);
      p_stdout = new std::ifstream(fd[READ]));
  }

Теперь, если подпроцесс не слишком много пишет в стандартный вывод, я могу дождаться его завершения и затем прочитать стандартный вывод.от p_stdout.Если он пишет слишком много, запись блокирует, и родитель ждет этого навсегда.Чтобы это исправить, я попытался подождать с WNOHANG в родительском объекте, если он еще не закончен, прочитать все доступные выходные данные из p_stdout с помощью readsome, немного поспать и повторить попытку.К сожалению, readsome никогда ничего не читает:

while (true)
  {
    if (waitid(P_PID, p_pid, &info, WEXITED | WNOHANG) != 0)
      throw ...;
    else if (info.si_pid != 0) // waiting has succeeded
      break;

    char tmp[1024];
    size_t sizeRead;
    sizeRead = p_stdout->readsome(tmp, 1024);
    if (sizeRead > 0)
      s_stdout.write(tmp, sizeRead);
    sleep(1);
  }

Вопрос: почему это не работает и как я могу это исправить?

edit: Если естьis only child, простое использование read вместо readsome, вероятно, сработает, но процесс имеет несколько дочерних элементов и должен реагировать, как только один из них завершится.

Ответы [ 2 ]

3 голосов
/ 08 декабря 2011

Как предположил sarnold, вам нужно изменить порядок своих звонков.Читайте сначала, подождите в последнюю очередь.Даже если ваш метод сработал, вы можете пропустить последнее чтение.то есть вы выходите из цикла до того, как прочитаете последний набор байтов, который был записан.

Проблема может заключаться в том, что ifstream не блокирует.Мне никогда не нравились iostreams, даже в моих проектах на C ++, мне всегда нравилась простота функций stdio C (т.е. FILE *, fprintf и т. Д.)Один из способов обойти это - читать, если дескриптор читабелен.Вы можете использовать select, чтобы определить, есть ли данные, ожидающие в этом канале.Вам понадобится выбрать, если вы все равно собираетесь читать от нескольких детей, так что, возможно, выучите это сейчас.

Что касается быстрой читаемой функции, попробуйте что-то вроде этого (обратите внимание, у меня нетпопытался скомпилировать это):

bool isreadable(int fd, int timeoutSecs)
{
    struct timeval tv = { timeoutSecs, 0 };
    fd_set readSet;
    FD_ZERO(&readSet);
    return select(fds, &readSet, NULL, NULL, &tv) == 1;
}

Затем в родительском коде сделайте что-то вроде:

while (true) {
    if (isreadable(fd[READ], 1)) {
        // read fd[READ];
        if (bytes <= 0)
            break;
    }
}

wait(pid);
1 голос
/ 08 декабря 2011

Я бы предложил переписать код так, чтобы он не вызывал waitpid(2), пока после read(2) не вызовет канал, возвращающий 0, чтобы обозначить конец файла. Как только вы получите возврат конца файла от ваших вызовов чтения, вы знаете, что ребенок мертв, и вы можете наконец waitpid(2) за него.

Другой вариант - еще больше отсоединить чтение от пожинания и выполнить вызовы ожидания в обработчике сигнала SIGCHLD асинхронно с операциями чтения.

...