execvp () вызывая EXC_SOFTWARE и причудливый цикл cin.getline? - PullRequest
0 голосов
/ 05 февраля 2011

Я запускаю некоторый код на Mac OSX 10.6.6 и XCode 3.2.4, и у меня есть довольно стандартный код: fork (), если pid == 0, тогда execvp с командой и аргументами (аргументы включают команда в качестве первого элемента в массиве, и массив завершается нулем).

Мы рассмотрим это в моем классе «Операционные системы», и наша задача - написать простую оболочку. Запускайте команды с их аргументами и переключателями, как перенаправления (<и>), так и pipe (|). У меня несколько проблем.

1) Иногда я получаю сигнал EXC_SOFTWARE во время отладки (до сих пор я не получил его, если я запускаю приложение за пределами XCode, но я новичок в Mac и не знаю, как это будет выглядеть, если я сделал)

2) Иногда getline для следующей команды становится ненужным, который, кажется, печатается другими кутами. Это начинается цикл навсегда, экспоненциально ломая. Я тестировал печать getpid () с каждой подсказкой, и только начальный процесс печатает их, у меня, похоже, нет случайной «бомбы-вилки».

Вот что у меня есть:

#include <iostream>
#include <string>
#include <unistd.h>

using namespace std;

char** Split(char* buffer, int &count) {
    count = 1;
    for (int i = 0; i < strlen(buffer); i++) {
        if (buffer[i] == ' ') {
            count++;
        }
    }
    const char* delim = " ";
    char* t = strtok(buffer, delim);
    char** args = new char*[count + 1];
    for (int i = 0; i < count; i++) {
        args[i] = t;
        t = strtok(NULL, delim);
    }
    args[count] = 0;
    return args;
}

void Run(char** argv, int argc) {
    int pid = 0;
    if ((pid = fork()) == 0) {
        //for testing purposes, print all of argv
        for (int i = 0; i < argc; i++) {
            cout << "{" << argv[i] << "}" << endl;
        }
        execvp(argv[0], argv);
        cout << "ERROR 1" << endl;
        exit(1);
    } else if (pid < 0) {
        cout << "ERROR 2" << endl;
        exit(2);
    }
    wait(NULL);
}

int main(int argc, char * const argv[]) {
    char buffer[512];
    char prompt[] = ":> ";
    int count = 0;
    while (true) {
        cout << prompt;
        cin.getline(buffer, 512);
        char **split = Split(buffer, count);
        Run(split, count);
    }
}

Это именно то, что у меня есть, вы должны уметь вырезать, вставлять и строить.

Я не лучший в C ++, и есть вероятность утечки памяти, когда я не удаляю split, но мой главный фокус - сигнал EXC_SOFTWARE и посмотрите, что я делаю неправильно с моей проблемой зацикливания. Есть мысли?

EDIT:

Назначение требует очень ограниченной проверки ошибок, и я предполагаю, что все данные введены правильно. Под правильным я подразумеваю правильно отформатированный и ограниченный для моего приложения для выполнения команды, то есть без странного подсчета пространства, без & для асинхронного запуска, без многопоточных команд и т. Д.

Ответы [ 2 ]

0 голосов
/ 05 февраля 2011

Одной из проблем является то, что вы не проверяете возврат из cin.getline(), поэтому, если вы вводите EOF, код идет в узкий цикл. Вы также теряете память.

Попробуйте:

while (cout << prompt && cin.getline(buffer, sizeof(buffer))
{
    int count = 0;
    char **split = Split(buffer, count);
    Run(split, count);
    delete[] split;
}

Код в Split() не совсем хорошо обрабатывает пустые строки. Кажется, что для запуска execvp() требуется эон, когда единственными аргументами являются нулевые указатели, что и происходит, если вы возвращаете пустую строку.


Я могу запустить несколько простых команд (таких как 'vim makefile', 'make shell', 'ls -l' и 'cat shell.cpp' и т. Д. - я даже выполнил несколько с более чем двумя аргументы) Хорошо с этим, и я могу выйти из команды (оболочки) с Control-D и так далее. Я исправил это так, что он компилируется без предупреждений от g++ -O -Wall -o shell shell.cpp. Я не исправил код разделения, чтобы он правильно обрабатывал пустые или все пустые строки.

#include <iostream>
#include <string>
#include <unistd.h>

using namespace std;

char** Split(char* buffer, int &count) {
    count = 1;
    for (size_t i = 0; i < strlen(buffer); i++) {  // #1
        if (buffer[i] == ' ') {
            count++;
        }
    }
    char** args = new char*[count + 1];
    const char* delim = " ";
    char* t = strtok(buffer, delim);
    for (int i = 0; i < count; i++) {
        args[i] = t;
        t = strtok(NULL, delim);
    }
    args[count] = 0;
    return args;
}

void Run(char** argv, int argc) {
    int pid = 0;
    if ((pid = fork()) == 0) {
        //for testing purposes, print all of argv
        for (int i = 0; i < argc; i++)
        {
            if (argv[i] != 0)  // #2
                cout << "{" << argv[i] << "}" << endl;
            else
                cout << "{ NULL }" << endl;  // #3
        }
        execvp(argv[0], argv);
        cout << "ERROR 1" << endl;
        exit(1);
    } else if (pid < 0) {
        cout << "ERROR 2" << endl;
        exit(2);
    }
    wait(NULL);
}

int main(int argc, char * const argv[]) {
    char buffer[512];
    char prompt[] = ":> ";
    while (cout << prompt && cin.getline(buffer, sizeof(buffer)))  // #4
    {
        int count = 0;
        char **split = Split(buffer, count);
        if (count > 0)  // #5
            Run(split, count);
        delete[] split;  // #6
    }
}

Я отметил значительные изменения (в основном они не такие большие). Я компилирую с GCC 4.2.1 на MacOS X 10.6.6.

Я не могу с готовностью объяснить символы мусора, которые вы видите в буфере.

0 голосов
/ 05 февраля 2011

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

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

...