我在Mac OSX 10.6.6和XCode 3.2.4上运行了一些代码,我有一些非常标准的代码:fork(),如果pid == 0则execvp带有命令和args(args包括command作为数组中的第一个元素,并且数组以null结尾。)
我们在我的操作系统类中讨论这个问题,我们的任务是编写一个简单的shell。使用args和开关运行命令,重定向(<和>)和管道(|)。我遇到了几个问题。
1)有时我在调试时得到EXC_SOFTWARE信号(到目前为止,如果我在XCode之外运行应用程序,我还没有得到它,但我是Mac的新手,如果我不知道那会是什么样子所做的那样)
2)有时下一个命令的getline会被其他cout打印出来的垃圾。这开始永远循环,指数地打破。我已经测试了打印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信号,看看我的循环问题出了什么问题。有什么想法吗?
编辑:
分配需要非常有限的错误检查,我假设所有输入都是正确的。通过正确我的意思是正确格式化和限制我的应用程序运行命令,即没有奇怪的空间计数,没有&amp;运行异步,没有多管道命令等。
答案 0 :(得分:0)
您假设输入行包含一个比空格更多的标记。如果输入行为空,以空格结束或开始或包含多个连续空格,则此假设可能会失败。在这些情况下,对strtok的一个调用将返回NULL,当您尝试在Run中打印该参数时,这将使分叉进程崩溃。这是我遇到问题的唯一案例;如果您遇到其他人,请说明您的意见。
为了避免这种假设,你可以用strtok进行计数,就像你进行标记化一样。这通常是一个好主意:如果你需要两件事情重合并且你可以用同样的方式完成它们,那么如果你采用不同的方式,那么你会引入一些额外的错误来源。
答案 1 :(得分: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()
中的代码并不能真正处理空白行。当唯一的参数是空指针时,似乎需要运行execvp()
,如果返回一个空行就会发生这种情况。
我能够运行多个简单的命令(例如'vim makefile'和'make shell'以及'ls -l'和'cat shell.cpp'等等 - 我甚至做了一些超过两个的命令参数)好吧,我可以用 Control-D 退出命令(shell),依此类推。我已修复它,因此编译时没有来自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
}
}
我已经标记了重大变化(它们大多不是那么大)。我正在使用Mac OS X 10.6.6上的GCC 4.2.1进行编译。
我无法轻易解释您在缓冲区中看到的垃圾字符。