Написание собственной оболочки - код зависает при обработке определенных каналов - в C - PullRequest
2 голосов
/ 19 ноября 2010

В настоящее время я пишу свою собственную оболочку как проект для класса, и у меня все практически работает. Моя проблема с моими трубами, иногда они работают, а иногда просто зависают, пока я их не перебью. Я провел исследование по этому вопросу, и кажется, что функция, в которую записывается его стандартный ввод, не получает EOF от первого процесса; обычно, как я понял, проблема в том, что канал не закрывается, но это не так (насколько мне известно) с моим кодом.

Все работы по перенаправлению и любые их изменения:

  • ls -l > file1
  • wc < file1 > file2

Работают следующие конвейерные команды:

  • w | head -n 4
  • w | head -n 4 > file1

Это не работает: ls | grep file1 показывает правильный вывод и никогда не заканчивается, пока пользователь не отправит ему сигнал прерывания. ls | grep file1 > file2 тоже не работает. Он зависает, не показывая вывод, создает файл2, но никогда не записывает в него.

Во всяком случае, я надеюсь, что мне чего-то не хватает, что кто-то еще может заметить; Я был в этом некоторое время. Дайте мне знать, если есть еще код, который я могу предоставить. Код, который я разместил ниже, является основным файлом, ничего не удалено.

/*
 * This code implemenFts a simple shell program
 * At this time it supports just simple commands with 
 * any number of args.
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>

#include "input.h"
#include "myShell.h"
#include "BackgroundStack.h"

/*
 * The main shell function
 */ 
main() {
    char *buff[20];
    char *inputString;

    BackgroundStack *bgStack = malloc(sizeof(BackgroundStack));
    initBgStack(bgStack);

    struct sigaction new_act;
    new_act.sa_handler = sigIntHandler;
    sigemptyset ( &new_act.sa_mask );
    new_act.sa_flags = SA_RESTART;
    sigaction(SIGINT, &new_act, NULL);

    // Loop forever
    while(1) {
        const char *chPath;

        doneBgProcesses(bgStack);

        // Print out the prompt and get the input
        printPrompt();

        inputString = get_my_args(buff);
        if (buff[0] == NULL) continue;

        if (buff[0][0] == '#') continue;

        switch (getBuiltInCommand(buff[0])) {
            case EXIT:
                exit(0);
                break;
            case CD:
                chPath = (buff[1]==NULL) ? getenv("HOME") : buff[1];
                if (chdir(chPath) < 0) {
                    perror(": cd");
                }
                break;
            default:
                do_command(buff, bgStack);
        }

        //free up the malloced memory
        free(inputString);
    }// end of while(1)
}

static void sigIntHandler (int signum) {}

/* 
 * Do the command
 */
int do_command(char **args, BackgroundStack *bgStack) {
    int status, statusb;  
    pid_t child_id, childb_id;
    char **argsb;
    int pipes[2];

    int isBgd = isBackgrounded(args);
    int hasPipe = hasAPipe(args);

    if (isBgd) removeBackgroundCommand(args);
    if (hasPipe) {
        int cmdBi = getSecondCommandIndex(args);
        args[cmdBi-1] = NULL;
        argsb = &args[cmdBi];
        pipe(pipes);
    }

    // Fork the child and check for errors in fork()
    if((child_id = fork()) == -1) {
        switch(errno) {
            case EAGAIN:
                perror("Error EAGAIN: ");
                return;
            case ENOMEM:
                perror("Error ENOMEM: ");
                return;
        }
    }

    if (hasPipe && child_id != 0) {
        childb_id = fork();
        if(childb_id == -1) {
            switch(errno) {
                case EAGAIN:
                    perror("Error EAGAIN: ");
                    return;
                case ENOMEM:
                    perror("Error ENOMEM: ");
                    return;
            }
        }
    }

    if(child_id == 0 || (childb_id == 0 && hasPipe)) {
        if (child_id != 0 && hasPipe) args = argsb;
        if (child_id == 0 && isBgd) {
            struct sigaction new_act;
            new_act.sa_handler = SIG_IGN;
            sigaction(SIGINT, &new_act, 0);
        }

        if (child_id == 0 && hasPipe) {
            if (dup2(pipes[1], 1) != 1) fatalPerror(": Pipe Redirection Output Error");
            close(pipes[0]);
            close(pipes[1]);
        }
        if (child_id != 0 && hasPipe) {
            if (dup2(pipes[0], 0) != 0) fatalPerror(": Pipe Redirection Input Error");
            close(pipes[0]);
            close(pipes[1]);
            waitpid(child_id, NULL, 0);
        }

        if ((child_id != 0 && hasPipe) || !hasPipe) {
            if (hasAReOut(args)) {
                char outFile[100];
                getOutFile(args, outFile);

                int reOutFile = open(outFile, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE);
                if (reOutFile<0) fatalPerror(": Redirection Output Error");

                if (dup2(reOutFile,1) != 1) fatalPerror(": Redirection Output Error");
                close(reOutFile);
            }
        }

        if ( (child_id == 0 && hasPipe) || !hasPipe) {
            if (hasAReIn(args)) {
                char inFle[100];
                getInFile(args, inFle);

                int reInFile = open(inFle, O_RDWR);
                if (reInFile<0) fatalPerror(": Redirection Input Error");

                if (dup2(reInFile,0) != 0) fatalPerror(": Redirection Input Error");
                close(reInFile);
            } else if (isBgd && !hasPipe) {
                int bgReInFile = open("/dev/null", O_RDONLY);
                if (bgReInFile<0) fatalPerror(": /dev/null Redirection Input Error");

                if (dup2(bgReInFile,0) != 0) fatalPerror(": /dev/null Redirection Input Error");
                close(bgReInFile);
            }
        }

        // Execute the command
        execvp(args[0], args);
        perror(args[0]);

        exit(-1);
    }

    // Wait for the child process to complete, if necessary
    if (!isBgd) waitpid(child_id, &status, 0);
    else if (!hasPipe) {
        printf("Child %ld started\n", (long)child_id);
        BackgroundProcess *bgPrs = malloc(sizeof(BackgroundProcess)); 
        bgPrs->pid = child_id;
        bgPrs->exitStatus = -1;

        addProcessToBgStack(bgStack, bgPrs);
    }
    if (hasPipe) waitpid(childb_id, &statusb, 0);
    if ( WIFSIGNALED(status) && !isBgd )    printf("Child %ld terminated due to signal %d\n", (long)child_id, WTERMSIG(status) );
    if ( hasPipe && WIFSIGNALED(statusb) ) printf("Child %ld terminated due to signal %d\n", (long)childb_id, WTERMSIG(status) );

} // end of do_command

Ответы [ 2 ]

7 голосов
/ 19 ноября 2010

Второй дочерний элемент должен , а не дождаться выхода первого дочернего элемента - он должен просто начать работать сразу (он будет блокироваться до тех пор, пока первый дочерний элемент не выдаст какой-либо вывод), поэтому удалите его waitpid() выполнено childb. Вместо этого родительский процесс должен ждать обоих дочерних процессов (или, возможно, только второго). (Действительно, как отметил JeremyP, этот вызов waitpid() все равно не выполняется, поскольку childb не является родителем child).

Ваша проблема, однако, заключается в том, что родительский процесс поддерживает дескрипторы открытых файлов в конвейере. Прямо перед комментарием // Wait for the child process to complete, if necessary родительский процесс должен закрыть свои дескрипторы файла канала:

close(pipes[0]);
close(pipes[1]);

Дескриптор открытого файла в родительском элементе означает, что дочерний процесс grep никогда не видит EOF, поэтому он не завершается.

1 голос
/ 19 ноября 2010

Я не знаю ответа, но я заметил одну проблему.

Вы согласитесь, что условие для

if(child_id == 0 || (childb_id == 0 && hasPipe))

верно только для двух дочерних процессов, новнутри блока оператора if есть:

    if (child_id != 0 && hasPipe) {
        if (dup2(pipes[0], 0) != 0) fatalPerror(": Pipe Redirection Input Error");
        close(pipes[0]);
        close(pipes[1]);
        waitpid(child_id, NULL, 0);
    }

Вызов waitpid() неверен, поскольку он вызывается вторым дочерним элементом, чтобы дождаться первого дочернего элемента.Вероятно, сбой ECHILD возможен, поскольку первый ребенок не является дочерним по отношению к второму.

Что касается вашей реальной проблемы, я подозреваю, что это связано с тем, что команда grep не прекратит работупока его вход не будет закрыт.Может быть какое-то тупиковое состояние, которое останавливает это.Вам нужно запустить это в отладчике или ввести некоторые данные, чтобы увидеть, где висит родительский процесс.

Редактировать

Ответ caf говорит нам обо всем.

Я предполагал, что ввод в grep был закрыт, потому что ls закроет свой вывод, когда он завершится, но, конечно, родительский процесс также имеет открытый дескриптор входного файла grep.Версия, использующая head, работает правильно, потому что head -n 4 завершается после четырех строк независимо от того, закрыт дескриптор входного файла или нет.

...