Как вы используете dup2 и fork вместе? - PullRequest
6 голосов
/ 14 января 2012

Я учусь на курсе по операционным системам, и мне тяжело, как ввод перенаправляется с dup2, когда у тебя есть вилки. Я написал эту небольшую программу, чтобы попытаться понять ее, но мне не удалось передать результаты внука ребенку. Я пытаюсь имитировать команду unix: ps -A | туалет Я новичок в Unix, но я считаю, что это должно подсчитать строки в списке запущенных процессов, которые я получаю. Поэтому мой вывод должен быть одним числом.

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>

using namespace std;

int main( int argc, char *argv[] )  {

    char *searchArg = argv[ 1 ];
    pid_t pid;

    if ( ( pid = fork() ) > 0 ) {
        wait( NULL );
        cout << "Inside parent" << endl;
    } 
    else if ( pid == 0 ) {
            int fd1[ 2 ];
            pipe( fd1 );
            cout << "Inside child" << endl;

            if ( pid = fork() > 0 ) {
                dup2( fd1[ 0 ], 0 );
                close( fd1[ 0 ] );
                execlp( "/bin/wc", "-l", NULL );
            }
            else if ( pid == 0 ) {
                cout << "Inside grand child" << endl;
                execlp( "/bin/ps", "-A", NULL );
            }
        }
    return 0;
}

У меня нет этого кода выше, но вот мое предположение о том, как все должно пойти вниз:

  • Нам нужно перенаправить стандартный вывод команды ps -A (что обычно выводится на экран, верно?), Чтобы команда wc -l могла использовать его для подсчета строк.
  • Этот стандартный вывод может быть перенаправлен с использованием dup2, например dup2 (?, 1), что означает перенаправление стандартного вывода на?. Тогда вы закрываете?.

Вопрос: Куда мне его перенаправить? Я знаю, что это должен быть один из дескрипторов файлов, но куда его следует перенаправить, чтобы wc мог его обработать?

  • wc каким-то образом получает стандартный вывод.

Вопрос: Как wc получает вывод? Через параметр execlp? Или операционная система проверяет один из дескрипторов файлов?

  • Выполнить wc -l.

Какой из них закрыт и оставлен открытым для wc для получения и обработки вывода ps? Я продолжаю думать, что об этом нужно думать задом наперед, так как ps должен передавать свой вывод wc ... но это, кажется, не имеет смысла, так как и дочерний, и внучатый обрабатываются параллельно.

pipe dream

1 Ответ

4 голосов
/ 14 января 2012

Прежде всего, давайте исправим ваш код, чтобы мы добавили к нему чуть больше проверки ошибок, и чтобы он работал;замените нижний бит на:

else if ( pid == 0 ) {
        int fd1[ 2 ];
        pipe( fd1 );
        cout << "Inside child" << endl;

        if ( pid = fork() > 0 ) {
            if (dup2( fd1[ 0 ] , 0 ) < 0) {
              cerr << "Err dup2 in child" << endl;
            }
            close( fd1[ 0 ] );
            close( fd1[ 1 ] ); // important; see below
            // Note: /usr/bin, not /bin
            execlp( "/usr/bin/wc", "wc", "-l", NULL );
            cerr << "Err execing in child" << endl;
        }
        else if ( pid == 0 ) {
            cout << "Inside grand child" << endl;
            if (dup2( fd1[ 1 ] , 1 ) < 0) {
              cerr << "Err dup2 in gchild" << endl;
            }
            close( fd1[ 0 ] );
            close( fd1[ 1 ] );
            execlp( "/bin/ps", "ps", "-A", NULL );
            cerr << "Err execing in grandchild" << endl;
        }
}

Теперь ваши вопросы:

  • Вопрос: Куда мне его перенаправить?Я знаю, что это должен быть один из файловых дескрипторов, но куда его следует перенаправить, чтобы wc мог его обработать?

    Файловые дескрипторы 0, 1 и 2 специально для Unix в этом отношении.Это стандартный ввод, стандартный вывод и стандартная ошибка.wc читает со стандартного ввода, поэтому, что бы ни было dup ed до 0.

  • Вопрос: Как wc получает вывод?Через параметр execlp?Или операционная система проверяет один из дескрипторов файлов?

    Как правило, после того, как процесс поменял свое изображение на exec, он будет иметь все открытые дескрипторы файлов, которые были до exec.(За исключением тех дескрипторов с установленным флагом CLOSE_ON_EXEC, но пока игнорируйте это). Поэтому, если вы dup2 что-то для 0, тогда wc будет читать его.

  • Какой из них закрыт и оставлен открытым для wc, чтобы получать и обрабатывать вывод ps?

    Как показано выше, вы можете закрыть оба конца канала как у потомка, так и у внука, и это будет хорошо,На самом деле, стандартная практика рекомендовала бы вам это сделать.Тем не менее, единственная действительно необходимая строка close в этом конкретном примере - это та, которую я комментирую как "важный" - это закрывает конец write канала в дочернем элементе.

    Идеязаключается в следующем: у обоих детей и внуков оба конца трубы открыты, когда они начинают.Теперь через dup мы подключили wc к концу чтения канала.wc будет продолжать посасывать этот канал до тех пор, пока все дескрипторы на конце записи канала не будут закрыты, после чего он увидит, что он достиг конца файла и остановится.Теперь, в внуке, мы можем не закрывать что-либо, потому что ps -A ничего не собирается делать ни с одним из дескрипторов, но записывает в дескриптор 1, и после того, как ps -A заканчивает выплевывать вещи онекоторые процессы он завершит, закрыв все, что имел.В этом случае нам не нужно закрывать дескриптор чтения, хранящийся в fd[0], потому что wc не будет пытаться читать из чего-либо, кроме дескриптора 0.Однако нам нужно закрыть конец записи канала в дочернем элементе, потому что в противном случае wc просто будет сидеть там с каналом, который никогда не будет полностью закрыт.

    Как вы можете видеть, причина, почемунам действительно не нужны были какие-либо строки close, за исключением того, что помеченные как "важные" зависят от деталей того, как wc и ps будут вести себя, поэтому стандартная практика - закрывать конец каналавы не используете полностью, и держите открытым конец, который вы используете только с одним дескриптором.Поскольку вы используете dup2 в обоих процессах, это означает четыре close оператора, как указано выше.

EDIT: обновлены аргументы до execlp.

...