0

我在 Mac OSX 10.6.6 和 XCode 3.2.4 上运行一些代码,并且我有一些非常标准的代码:fork(), if pid == 0 then execvp with a command and the args (args include the command as the数组中的第一个元素,并且数组以 null 结尾)。

我们将在我的操作系统课上讨论这个问题,我们的任务是编写一个简单的 shell。运行带有参数和开关的命令,包括重定向(< 和 >)和管道(|)。我遇到了几个问题。

1)有时我在调试时收到 EXC_SOFTWARE 信号(到目前为止,如果我在 XCode 之外运行应用程序,我还没有收到它,但我是 Mac 新手,不知道如果我这样做会是什么样子)

2) 有时,下一个命令的 getline 会变成垃圾,似乎是由其他 couts 打印的。这开始永远循环,呈指数级中断。我已经测试过在每个提示符下打印 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 信号,看看我在循环问题上做错了什么。有什么想法吗?

编辑:

分配需要非常有限的错误检查,我假设所有输入都是正确的。正确我的意思是正确格式化和限制我的应用程序运行命令,即没有奇怪的空间计数,没有 & 运行异步,没有多管道命令等。

4

2 回答 2

0

您假设输入行包含比空格多一个标记。如果输入行为空、以空格结尾或开始或包含多个连续空格,则此假设可能会失败。在这些情况下,对 strtok 的调用之一将返回 NULL,当您尝试在 Run 中打印该参数时,这将导致分叉进程崩溃。这些是我遇到问题的唯一案例;如果您遇到任何其他人,请指定您的输入。

为了避免这种假设,您可以像进行标记化一样使用 strtok 进行计数。这通常是一个好主意:如果你需要两件事情同时发生并且你可以用同样的方式来做,那么如果你用不同的方式来做,你就会引入一个额外的错误来源。

于 2011-02-05T03:30:51.290 回答
0

一个问题是您没有检查从 的返回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()根本不能很好地处理空行。当唯一的参数是空指针时,似乎需要一个 aeon 来运行execvp(),如果你返回一个空行,就会发生这种情况。


我能够运行多个简单的命令(例如 'vim makefile' 和 'make shell' 和 'ls -l' 和 'cat shell.cpp' 等等 - 我什至用两个以上的参数做了一些)有了这个,我可以退出命令(shell)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
    }
}

我已经标记了显着的变化(它们大多不是那么大)。我在 MacOS X 10.6.6 上使用 GCC 4.2.1 进行编译。

我无法轻易解释您在缓冲区中看到的垃圾字符。

于 2011-02-05T03:39:31.107 回答