Не получить все строки из трубы

Я работаю над заданием, в котором мне нужно несколько процессов (родительский и дочерний) для общения. Родитель отправляет пути к файлам детям, и они должны запустить на них linux file (/usr/bin/file), возвращая вывод отцу.

На данный момент я все еще пытаюсь заставить одного ребенка работать, так что пока я предполагаю, что есть один ребенок.

Я собираюсь отправить несколько путей к файлам каждому ребенку (пакет файлов), а затем прочитать вывод file.

Проблема:

Я использую цикл для write нескольких путей к файлам, но когда я read выходной канал дочернего элемента, я не получаю весь вывод, который должен.

Код:

#define Read            0
#define Write           1
#define ParentRead      read_pipe[0]
#define ParentWrite     write_pipe[1]
#define ChildRead       write_pipe[0]
#define ChildWrite      read_pipe[1]
#define PIPE_BUF_LEN    4096

using namespace std;
int main()
{
    /** Pipe for reading for subprocess */
    int read_pipe[2];
    /** Pipe for writing to subprocess */
    int write_pipe[2];
    char buffer[PIPE_BUF_LEN] = "";
    if (pipe(read_pipe) == 0 && pipe(write_pipe) == 0)
    {
        pid_t pid = fork();
        if (pid == -1)
        {
            fprintf(stderr, "Fork failure");
            exit(EXIT_FAILURE);
        }
        else if (pid == 0) //Child process
        {
            close(ParentRead);
            close(ParentWrite);
            dup2 (ChildRead, STDIN_FILENO); /*redirect ChildRead to stdin*/
            dup2 (ChildWrite, STDOUT_FILENO); /*redirect stdout to ChildWrite*/
            char* paramArgs[]={"/usr/bin/file","-n","-f-",NULL};
            execv("/usr/bin/file",paramArgs);
            exit(EXIT_FAILURE);
        }
        else { //Parent process
            close(ChildRead);
            close(ChildWrite);

            for (int i=0; i < 3 ;i++)
            {
                /*write to processes which are ready for writing: */
                fd_set rfds;
                int retval;
                FD_ZERO(&rfds);
                FD_SET(ParentWrite, &rfds);
                retval = select(10, NULL, &rfds, NULL, NULL);
                if (retval == -1)
                {
                    perror("select()");
                }
                else if (retval)
                {
                    write(ParentWrite, "file1\nfile2\n", 12);
                }
                /*read from processes which are ready for reading*/
                FD_ZERO(&rfds);
                FD_SET(ParentRead, &rfds);
                retval = select(10, &rfds, NULL, NULL, NULL);
                if (retval == -1)
                {
                    perror("select()");
                }
                else if (retval)
                {
                    read(ParentRead, buffer, PIPE_BUF_LEN);
                    cout << buffer;
                }
            }
        }
    }
    exit(EXIT_SUCCESS);
}

В этом случае я пытаюсь запустить file для файла1\nфайл2\n (обратите внимание на используемые флаги -n -f-) в цикле из 3 итераций, ожидая получить шесть строк, но получаю только три:

файл1: ОШИБКА: невозможно открыть «файл1» (нет такого файла или каталога)

файл2: ОШИБКА: невозможно открыть «файл2» (нет такого файла или каталога)

файл1: ОШИБКА: невозможно открыть «файл1» (нет такого файла или каталога)

Когда я не перенаправляю вывод дочернего элемента в канал (позволяя ему писать в std_out), я получаю все шесть строк.

Любая помощь будет оценена.


person Paz    schedule 12.03.2014    source источник
comment
Вы не гарантируете, что получите столько байтов, сколько запрашиваете при чтении. Если вы распечатаете, сколько байтов читает родитель, вы увидите, что вы получаете короткие чтения.   -  person Duck    schedule 13.03.2014
comment
Да, но почему я получаю эти короткие чтения? Как я могу получить все строки, выведенные вызовами файла?   -  person Paz    schedule 13.03.2014


Ответы (1)


Планировщик. Вы находитесь в цикле, который просит дочерний элемент (то есть pgm file) просмотреть два файла три раза. Вы делаете только одно чтение за цикл в родительском.

  • Если дочерний процесс запланирован и не прерывается, он выполняет первый поиск файла, записывает в канал, выполняет второй поиск и записывает в канал. Родитель получает расписание, и вы читаете полный вывод дочернего элемента за одно чтение. Ты золотой (но удачливый).
  • Ребенок запланирован, выполняет первый поиск и запись в канал и прерывается. Родитель читает один вывод. Ребенок перепланируется, выполняет второй поиск и записывает канал. Родитель читает один вывод. Поскольку вы читаете только 3 раза, вы в конечном итоге читаете только 3 из 6 выходов.
  • Вариации вышеописанного. Может быть, вы прочитаете 1 вывод, затем 3, затем 1. Вы просто не знаете.

Обратите внимание, что причина, по которой вы, по крайней мере, получаете полный дочерний вывод при каждом чтении, заключается в том, что записи в канал гарантированно будут атомарными, если они меньше длины PIPE_BUF, что и есть. Если вы использовали такую ​​подпрограмму, скажем, для сокета, вы можете получить некоторую дробную часть сообщения при каждом чтении.

Решение. Вы должны читать в цикле, пока не получите ожидаемое количество байтов (или полное сообщение). В этом случае я предполагаю, что вывод file всегда представляет собой одну строку, заканчивающуюся новой строкой. Но вы запрашиваете два выхода file каждый раз через цикл. Поэтому читайте до тех пор, пока это условие не будет выполнено, а именно, пока вы не прочитаете две полные строки.

person Duck    schedule 13.03.2014