Я начинаю кодировать оболочку в UNIX, чтобы практиковаться с вызовами API, такими как fork () dup2 (), read () и wait (). В настоящее время моя оболочка открывается и работает нормально. Когда я набираю команду для запуска, например ls -a, она правильно разбирает эту команду и выполняет ее. Проблема в том, что основной цикл завершается раньше времени, выходя из оболочки после одной команды. Мне нужно, чтобы цикл продолжался до тех пор, пока «exit» не будет прочитан из стандартного ввода. Вот мой текущий код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <unistd.h>
static const char prompt[] = "myshell> ";
static const char sep[] = " \t\n\r";
int main()
{
    int ac; // arg count
    char *av[10]; //argument vector
    int tty = open("/dev/tty", O_RDWR); // open tty for read/write
    int pid; // process id
    int status; // child process exit status
    int w;
    void (*istat)(int), (*qstat)(int);

    if (tty == -1)
    {
        fprintf(stderr, "can't open /dev/tty\n");
        exit(EXIT_FAILURE);
    }
    while (1)
    {
        char *arg, line[256]; // buffer to hold line of input
        int i;
        // prompt and read
        write(tty, prompt, sizeof(prompt) - 1);
        i = read(tty, line, sizeof(line));
        if (i <= 0)
            break;
        line[i] = '\0';
        // tokenize the line into av[]
        ac = 0;
        for (arg = strtok(line, sep); arg && ac < 10; arg = strtok(NULL, sep))
            av[ac++] = arg;

        if (ac > 0 && strcmp(av[0], "exit") == 0)
            break;

        if ((pid = fork()) == 0)
        {
            // this is the forked child process that is a copy of the running program
            dup2(tty, 0); // stdin from tty
            dup2(tty, 1); // stdout to tty
            dup2(tty, 2); // stderr to tty
            close(tty);
            // last argument must be NULL for execvp()
            av[ac] = NULL;
            // execute program av[0] with arguments av[0]... replacing this program
            execvp(av[0], av);
            fprintf(stderr, "can't execute %s\n", av[0]);
            exit(EXIT_FAILURE);
        }
        close(tty);
        // disable interrupt (^C and kill -TERM) and kill -QUIT
        istat = signal(SIGINT, SIG_IGN);
        qstat = signal(SIGQUIT, SIG_IGN);
        // wait until forked child process terminated, get its exit status
        while ((w = wait(&status)) != pid && w != -1)
            continue;
        if (w == -1)
            status = -1;

    }

    // restore interrupt and quit signals
    signal(SIGINT, istat);
    signal(SIGQUIT, qstat);
    exit(EXIT_SUCCESS);
}

Я попытался переместить эти строки прямо над успешным выходом (так что они вне цикла и внутри основного)

    close(tty);
    // disable interrupt (^C and kill -TERM) and kill -QUIT
    istat = signal(SIGINT, SIG_IGN);
    qstat = signal(SIGQUIT, SIG_IGN);
    // wait until forked child process terminated, get its exit status
    while ((w = wait(&status)) != pid && w != -1)
        continue;
    if (w == -1)
        status = -1;

}

// restore interrupt and quit signals
    signal(SIGINT, istat);
    signal(SIGQUIT, qstat);
    exit(EXIT_SUCCESS);
}

Это правильно зацикливало оболочку, но выводило приглашение поверх последней строки вывода выполненной команды, потому что программа не ждала завершения дочернего процесса перед печатью приглашения и ожиданием ввода. Я попытался переместить некоторые строки, но каждый раз он просто завершал работу оболочки после выполнения одной команды

0
user9431482 13 Мар 2018 в 01:42

2 ответа

Лучший ответ

Вы не хотите вызывать close(tty) в основном цикле. Это приведет к сбою следующего read(tty,... и выходу из оболочки.

Кроме того, если вы действительно хотите отключить SIGQUIT / SIGQUIT, вы должны симметрично восстановить их внутри цикла.

2
Electron 12 Мар 2018 в 22:52

Следующий опубликованный код:

  1. пропускает обработку сигналов, вы можете добавить это обратно.
  2. чисто компилируется
  3. правильно проверяет и документирует любые ошибки
  4. выполняет желаемый функционал, за исключением отключения определенных сигналов
  5. продолжает выполнять цикл до тех пор, пока пользователь не войдет в "выход"

А теперь предлагаемый код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

// added next statement for 'wait()' and 'waitpid()'
#include <sys/types.h>

#include <sys/wait.h>
#include <unistd.h>
//#include <signal.h>
#include <unistd.h>

static const char prompt[] = "myshell> ";
static const char sep[] = " \t\n\r";


int main( void )
{
    int ac; // arg count
    char *av[10]; //argument vector

    pid_t pid; // process id
    int status; // child process exit status


    int tty = open("/dev/tty", O_RDWR); // open tty for read/write
    if (tty == -1)
    {
        perror( "open for /dev/tty failed");
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        char *arg;
        char line[256]; // buffer to hold line of input

        // prompt and read
        write(tty, prompt, sizeof(prompt) - 1);
        ssize_t i = read(tty, line, sizeof(line));

        if (i == 0)
            break;

        if (i < 0)
        {
            perror( "read failed" );
            exit( EXIT_FAILURE );
        }

        line[i] = '\0';

        // tokenize the line into av[]
        ac = 0;
        for (arg = strtok(line, sep); arg && ac < 10; arg = strtok(NULL, sep))
            av[ac++] = arg;

        // last argument must be NULL for execvp()
        av[ac] = NULL

        if (ac > 0 && strcmp( av[0], "exit" ) == 0)
            break;

        pid = fork();
        switch( pid )
        {
        case -1:
            perror( "fork failed" );
            exit( EXIT_FAILURE );
            break;

        case 0:  // child process
            // this is the forked child process that is a copy of the running program
            dup2(tty, 0); // stdin from tty
            dup2(tty, 1); // stdout to tty
            dup2(tty, 2); // stderr to tty
            close(tty);
            ;

            // execute program av[0] with arguments av[0]... replacing this program
            execvp(av[0], av);
            perror( "execvp failed" );
            exit(EXIT_FAILURE);
            break;

        default: // parent process
            // wait until forked child process terminated, get its exit status
            waitpid( pid, &status, 0 );
            break;
        }
    }

    return 0;
}
0
user3629249 13 Мар 2018 в 22:00