1

我在 fork() 之类的事情上遇到了一些麻烦。

我正在开发一个外壳,用户可以在其中编写将在普通和常见外壳中执行的命令。

我有一个这样的主要功能:

void Shell::init() {
    string command;
    while (1) {
        cout << getPrompt() << " ";
        command = readCommand();
        if (command.length() > 0) handleCommand(command);
    }
}

handleCommand()是几乎可以做所有事情的功能。在其中的某个地方,我有以下内容:

...
else {
    pid_t pid;
    pid = fork();

    char* arg[tokens.size() + 1];
    for (int i = 0; i < tokens.size(); ++i) {
        arg[i] = (char*) tokens[i].c_str();
    }
    arg[tokens.size()] = NULL;

    if (pid == 0) {
        if (execvp(tokens[0].c_str(), arg) == -1) {
            cout << "Command not known. " << endl;
        };
    } else {
        wait();
    }
}

我想要的是,当我到达那一点时,该命令将被视为程序调用,因此我创建了一个子项来运行它。它工作得几乎完美,但我在程序输出之前再次得到提示。例子:

tronfi@orion:~/NetBeansProjects/Shell2$ whoami
tronfi@orion:~/NetBeansProjects/Shell2$ tronfi

tronfi@orion:~/NetBeansProjects/Shell2$ 

孩子应该在 之后死亡execvp,所以它不应该调用提示,并且父母正在等到孩子死亡。

所以......我做错了什么?

谢谢!!

4

3 回答 3

3

我不确定这正是问题所在,但即使execvp()失败,您也必须确保孩子退出:

if (pid == 0) {
    if (execvp(tokens[0].c_str(), arg) == -1) {
        cout << "Command not known. " << endl;
    };
    exit(1); // or some other error code to indicate execvp() fails
} else {
    wait();
}

如果你不这样做,那么如果excecvp()失败,那么你最终会得到你的 shell 的两个实例,这可能不是你想要的。

于 2010-12-09T19:34:31.460 回答
3

你打错电话wait()了。它期望传递一个指向 int 的指针,其中将存储孩子的退出状态:

int status;
wait(&status);

但是,实际上,您应该使用它waitpid()来检查您所追求的特定孩子。waitpid()如果被信号打断,您还需要循环:

int r;
do {
    r = waitpid(pid, &status, 0);
} while (r < 0 && errno == EINTR);
于 2010-12-09T23:04:25.660 回答
0

必须使用调用终止孩子

退出(0)
(仅在成功时),因为这有助于清理内存并刷新缓冲区。孩子返回的这个状态必须由父母检查,然后只有它应该给出提示。

如果您需要更多详细信息,请告诉我。

于 2010-12-12T13:08:24.317 回答