Кодировка трубы "|"в C ++: почему пустая строка вводит getline после конвейера? - PullRequest
1 голос
/ 18 марта 2019

Я кодирую фиктивную оболочку, и сейчас я работаю над кодированием каналов с dup2.Вот мой код:

bool Pipe::execute() {
    int fds[2]; //will hold file descriptors
    pipe(fds);
    int status;
    int errorno;
    pid_t child;
    child = fork();

    if (-1 == child) {
        perror("fork failed");
    }
    if (child == 0) {
        dup2(fds[1], STDOUT_FILENO);
        close(fds[0]);
        close(fds[1]);
        this->component->lchild->execute();
        _exit(1);
    }
    else if (child > 0) {
        dup2(fds[0], STDIN_FILENO);
        close(fds[0]);
        close(fds[1]);
        this->component->rchild->execute();
        waitpid(child, &status, 0);
        if ( WIFEXITED(status) ) {
            //printf("child exited with = %d\n",WEXITSTATUS(status));
            if ( WEXITSTATUS(status) == 0) {
                cout << "pipe parent finishing" << endl;
                return true;
            }
        }
        return false;
    }
}

this->component->lchild->execute(); и this->component->rchild->execute(); запускают execvp по соответствующим командам.Я подтвердил, что каждый из них возвращается, распечатав заявление в родительском процессе.Однако в моем Pipe::execute() кажется, что дочерний процесс не завершается, потому что оператор cout в родительском процессе никогда не печатается, и я получаю ошибку сегментации после инициализации приглашения ($) (см. Рисунок).Вот основная функция, которая инициализирует приглашение после каждого выполнения:

int main()
{
    Prompt new_prompt;
    while(1) {
        new_prompt.initialize();
    }
    return 0;
}

, а вот функция initialize():

void Prompt::initialize()
{
    cout << "$ ";
    std::getline(std::cin, input);

    parse(input);
    run();
    input.clear();
    tokens.clear();
    fflush(stdout);
    fflush(stdin);
    return;
}

Кажется, что ls | sort работает нормально, нозатем, когда приглашение инициализируется, пустая строка считывается на вход getline.Я пытался использовать cin.clear (), cin.ignore, а также строки fflush и clear () выше.Эта пустая строка "анализируется", и затем вызывается функция run(), которая пытается разыменовать нулевой указатель.Любые идеи о том, почему / где эта пустая строка вводится в getline?и как мне решить это?Спасибо!

ОБНОВЛЕНИЕ: родительский процесс в конвейере завершается.Я также заметил, что я получаю ошибки seg также для моих классов перенаправления ввода / вывода (> и <).Я думаю, что я не сбрасываю поток или правильно закрываю файловые дескрипторы ...

Вот моя execute() функция для lchild и rchild:

bool Command::execute() {
    int status;
    int errorno;
    pid_t child;
    vector<char *> argv;
    for (unsigned i=0; i < this->command.size(); ++i) {
        char * cstr = const_cast<char*>(this->command.at(i).c_str());
        argv.push_back(cstr);
    }
    argv.push_back(NULL);
    child = fork();
    if (-1 == child) {
        perror("fork failed");
    }
    if (child == 0) {
        errorno = execvp(*argv.data(), argv.data());
        _exit(1);

    } else if (child > 0) {
        waitpid(child, &status, 0);
        if ( WIFEXITED(status) ) {
            //printf("child exited with = %d\n",WEXITSTATUS(status));
            if ( WEXITSTATUS(status) == 0) {
                //cout << "command parent finishing" << endl;
                return true;
            }
        }
        return false;
    }
}

1 Ответ

2 голосов
/ 18 марта 2019

Вот ошибка:

else if (child > 0) {
    dup2(fds[0], STDIN_FILENO);
    close(fds[0]);
    close(fds[1]);
    this->component->rchild->execute();

Вы закрываете стандартный ввод для родителя, а не только для правильного потомка.После этого stdin родительского процесса такой же, как и у правого потомка.

После этого

std::getline(std::cin, input);

Пытается прочитать вывод левого потомка, а не исходный stdin.,К этому моменту левый ребенок закончил, и этот конец трубы был закрыт.Это приводит к сбою чтения стандартного ввода и оставляет input без изменений в исходном состоянии.

Редактировать: Ваш дизайн имеет незначительные и серьезные недостатки.Небольшой недостаток в том, что вам не нужна вилка в Pipe::execute.Основной недостаток заключается в том, что именно ребенок должен перенаправлять потоки и закрывать дескрипторы.

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

...