Реализация оболочки в C - конвейерный ввод имеет правильный вывод, но выходит из цикла - PullRequest
0 голосов
/ 09 июня 2018

Я пытаюсь реализовать базовую оболочку в C, которая обрабатывает несколько каналов.Он ожидает ввода и выполняет команды в цикле for.Когда он получает EOF, он перестает ждать ввода и выходит.

В данный момент моя оболочка выводит правильный вывод при вводе конвейерной команды, например, ls | wc | grep ..., но перестает ждать ввода и завершаетсявнешний цикл while вместо ожидания следующей строки ввода.

Я обнаружил, что это происходит из-за того, что fgets в моем цикле while возвращает ноль (stdin как-то получает EOF?).Я не получаю никаких ошибок при создании разветвления, создании канала или исключении.

Однако, если я ввожу одну команду за раз без каких-либо каналов, например, ls, она успешно выведет правильный вывод и ожидает следующую строку ввода, как и должно быть.

Моя программа разбирает каждую строку ввода в struct перед попыткой выполнения каждой команды (опущено ниже).struct разработан таким образом, что я могу легко передать проанализированные аргументы в execvp, который я не буду здесь описывать.

Это сильно упрощенная версия моего кода, в которой большая часть проверки ошибок опущена:

FILE* input;
char line[MAX_LINE];

input = stdin;
printf("> ");
fflush(stdout);

while (fgets(line, sizeof(line), input)) {
    int i;
    struct cmdLine;
    /* struct defined elsewhere
    ** commands = # of commands in parsed input
    ** start = index where a command and its args start
    ** args[] = array holding each command/arg
    */

    /* parse input line into cmdLine */
    ...

    /* exec all commands in pipeline except the last */
    for (i = 0; i < cmdLine.commands-1; ++i) {
        int pd[2];
        pipe(pd);

        if (fork() == 0) {
            dup2(pd[1], 1);
            execvp(cmdLine.args[cmdLine.start[i]], &(cmdLine.args[cmdLine.start[i]]));
        } else {
            wait(NULL);
        }

        dup2(pd[0], 0);
        close(pd[1]);
    }    

    /* exec last command */
    if (fork() == 0) {
        execvp(cmdLine.args[cmdLine.start[i]], &(cmdLine.args[cmdLine.start[i]]));
    } else {
        wait(NULL);
    }

    if (stdin == input) {
        printf("> ");  /* print shell prompt */
        fflush(stdout);
    }
}

Я почти уверен, что где-то напутал со своим обманом, но я пытался часами, и я не понимаю, что я делаю неправильно.EOF как-то отправляется на стандартный ввод, так что fgets возвращает NULL?

1 Ответ

0 голосов
/ 09 июня 2018

Вызывая dup2 с 0 (= stdin) в качестве второго аргумента, вы закрываете исходный stdin в конце каждой итерации цикла for, поэтому вы больше не можете разговаривать сваша программа через оригинальный stdin.

Проблема в вашем коде состоит в том, что вы пытаетесь вручную соединить все каналы вместе с кем-то еще;это не сработает.Вот что должно работать:

  • Для n программ вам нужно как минимум (n-1) каналов.
  • Запись всех FD-каналов в массивах: по одному для входной стороныканал (который записывается), один для выходной стороны (который читается из).
  • Для каждого процесса, который вы разветвляете, подключите выход предыдущего канала (если есть) к его stdin, ивход следующего канала в его stdout (или stdout вашей основной программы, если вы обрабатываете последний процесс в вашей цепочке каналов).
  • Как только вы разветвите все: в цикле, poll() на выходных FD ваших каналов, прочитайте от любого, у которого есть активность, и запишите на вход следующего канала (ваш собственный stdout в конце).Если вы получаете EOF на одном из каналов, закройте вход следующего канала (и удалите выход EOF-канала из вашего выходного массива).Как только все FD будут закрыты, выйдите из цикла.

РЕДАКТИРОВАТЬ: Я просто подумал о другом, более простом способе, который требует меньше изменений кода, но я не думал, что полностью через.:) Проблема в том, что вы уничтожаете свой собственный стандарт.Если вы делаете все это (то есть весь «обрабатываете одну строку команд») в разветвленном дочернем элементе, замена stdin между процессами вообще не влияет на родительский процесс. Тем не менее, это потребует большой буферизации в ядре и т. Д.это, вероятно, не будет масштабироваться.

...