Использование posix pipe () и dup () с C ++ для перенаправления проблем ввода-вывода - PullRequest
3 голосов
/ 25 октября 2011

Мне нужно изменить простую оболочку, которую я написал для предыдущего домашнего задания, для обработки перенаправления ввода / вывода, и у меня возникают проблемы с работой каналов.Кажется, что когда я пишу и читаю в stdout и из stdin после дублирования файловых дескрипторов в отдельных процессах, конвейер работает, но если я использую что-то вроде printf, fprintf, gets, fgets и т. Д., Чтобы попытаться увидеть, является ли выводОбнаруженный в канале, он отправляется на консоль, хотя файловый дескриптор для stdin и stdout явно является копией канала (я не знаю, правильно ли это сформулировать, но, думаю, смысл ясен).

Я на 99,9% уверен, что я делаю все, как и должно быть, по крайней мере, в простом C - например, закрытие всех файловых дескрипторов соответствующим образом после dup () - и файловый ввод / вывод работает нормальнотак что это похоже на проблему детали, о которой я не знаю и не могу найти никакой информации.Я провел большую часть дня, пробуя разные вещи, и последние несколько часов гуглил, пытаясь выяснить, могу ли я перенаправить cin и cout на канал, чтобы посмотреть, исправит ли это это, но кажется, что это больше проблем, чем стоитэтот пункт.

Должно ли это работать путем простого перенаправления stdin и stdout, поскольку cin и cout должны синхронизироваться со stdio?Я подумал, что это должно произойти, тем более что команды, вероятно, написаны на C, поэтому они будут использовать stdio, я думаю.Однако, если я попробую команду типа «cat [file1] [file2] | sort», она выведет результат команды cat [file1] [file2] в командную строку, и сортировка не получит никакого ввода, поэтому она не имеетвыход.Понятно также, что dup () также не влияет на cout и cin, поэтому я соединил два и два вместе и пришел к такому выводу. Вот несколько укороченная версия моего кода, исключая все проверки на ошибки и тому подобное, которые яЯ уверен, что справляюсь хорошо.Я могу опубликовать полный код, если он дойдет до него, но это много, поэтому я начну с этого.

Я переписал функцию так, чтобы родительский элемент раздваивал дочерний элемент для каждой команды и связывал их с помощью каналов.по мере необходимости, а затем ждет, чтобы дочерние процессы умерли.Опять же, запись и чтение файловых дескрипторов 0 и 1 работают (то есть запись и чтение из канала), stdio в указателях FILE stdin и stdout не работают (не запись в pipe).

Спасибово многом это меня убивает ...

ОБНОВЛЕНИЕ: я не менял строковый cmd для каждой из разных команд, поэтому он не работал, потому что канал просто перешел к одной и той же командеитоговый результат был таким же ... Извините за глупость, но спасибо, потому что я нашел проблему с strace.

int call_execv( string cmd, vector<string> &argv, int argc,
    vector<int> &redirect)
{
    int result = 0, pid, /* some other declarations */;
    bool file_in, file_out, pipe_in, pipe_out;
    queue<int*> pipes; // never has more than 2 pipes

    // parse, fork, exec, & loop if there's a pipe until no more pipes
    do
    {
        /* some declarations for variables used in parsing */
        file_in = file_out = pipe_in = pipe_out = false;

        // parse the next command and set some flags
        while( /* there's more redirection */ )
        {
            string symbol = /* next redirection symbol */

            if( symbol == ">" )
            {
                /* set flags, get filename, etc */
            }   
            else if( symbol == "<" )
            {
                /* set flags, get filename, etc */
            }
            else if( pipe_out = (symbol == "|") )
            {
                    /* set flags, and... */
                int tempPipes[2];               
                pipes.push( pipe(tempPipes) );
                break;
            }
        }

        /* ... set some more flags ... */

        // fork child
        pid = fork();
        if( pid == 0 )  // child
        {
            /* if pipe_in and pipe_out set, there are two pipes in queue. 
            the old pipes read  is dup'd to stdin, and the new pipes
            write is dup'd to stdout, other two FD's are closed */

            /* if only pipe_in or pipe_out, there is one pipe in queue.
            the unused end is closed in whichever if statement evaluates */

            /* if neither pipe_in or pipe_out is set, no pipe in queue */

            // redirect stdout
            if( pipe_out ){
                // close newest pipes read end
                close( pipes.back()[P_READ] );

                // dup the newest pipes write end
                dup2( pipes.back()[P_WRITE], STDOUT_FILENO );

                // close newest pipes write end
                close( pipes.back()[P_WRITE] );
            }
            else if( file_out ) 
                freopen(outfile.c_str(), "w", stdout);

            // redirect stdin
            if( pipe_in ){
                close( pipes.front()[P_WRITE] );
                dup2( pipes.front()[P_READ], STDIN_FILENO );
                close( pipes.front()[P_READ] );
            }
            else if ( file_in ) 
                freopen(infile.c_str(), "r", stdin);


            // create argument list and exec
            char **arglist = make_arglist( argv, start, end );
            execv( cmd.c_str(), arglist );

            cout << "Execution failed." << endl;    
            exit(-1); // this only executes is execv fails

        }   // end child

        /* close the newest pipes write end because child is writing to it.
           the older pipes write end is closed already */
        if( pipe_out ) 
            close( pipes.back()[P_WRITE] );

        // remove pipes that have been read from front of queue
        if( init_count > 0 )    
        {
            close( pipes.front()[P_READ] ); // close FD first
            pipes.pop();                    // pop from queue
        }   

    } while ( pipe_out );

    // wait for each child process to die

    return result;
}

Ответы [ 2 ]

1 голос
/ 25 октября 2011

Вне зависимости от проблемы, вы не проверяете возвращаемые значения . Как узнать, успешно ли выполнена команда pipe() или dup2()? Вы убедились, что stdout и stdin действительно указывают на трубу прямо перед execv? Сохраняет ли execv дескрипторы файлов, которые вы ему даете? Не уверен, вот соответствующий параграф из execve документации:

  • По умолчанию файловые дескрипторы остаются открытыми через execve (). Файловые дескрипторы, помеченные как close-on-exec, закрываются; см. описание FD_CLOEXEC в fcntl (2). (Если дескриптор файла закрыт, это приведет к снятию всех блокировок записей, полученных для базового файла этим процессом. Подробности смотрите в fcntl (2).) POSIX.1-2001 говорит что если файловые дескрипторы 0, 1 и 2 в противном случае будут закрыты после успешного выполнения execve (), и процесс получит привилегию, потому что set-user_ID или set-group_ID Бит миссии был установлен в исполняемом файле, после чего система может открыть неопределенный файл для каждого из этих файловых дескрипторов. Как правило, нет переносимой программы, привилегированные или нет, можно предположить, что эти три файловых дескриптора останутся закрытыми в execve ().

Вы должны добавить дополнительные выходные данные отладки и посмотреть, что на самом деле происходит. Вы использовали strace -f (чтобы следить за детьми) в своей программе?

0 голосов
/ 26 октября 2011

следующее:

queue<int*> pipes; // never has more than 2 pipes
// ...
int tempPipes[2];               
pipes.push( pipe(tempPipes) );

Не должно работать. Не уверен, как он компилируется, так как результат pipe() равен int. Обратите внимание только на то, что tempPipes выходит из области видимости и его содержимое теряется.

Должно быть что-то вроде этого:

struct PipeFds
{
    int fds[2];
};

std::queue<PipeFds> pipes;
PipeFds p;
pipe(p.fds); // check the return value
pipes.push(p);
...