为什么这段代码没有被执行?
Why does this code not get executed?
我目前正在制作自己的 shell 程序。我必须保留用户输入的最后 10 个命令的历史记录。每当用户输入不是常规命令的命令(例如 history、hi、fakecommand 等)时,它就会被放入历史记录中。但是每当用户输入一个真正的命令(例如 ls、ps、top、cat 等)时,它都不会添加到历史记录中。
我认为这可能与 execvp 命令有关,因为我相信该命令会创建一个分支并让子进程执行该命令。但我不确定它是否应该这样做,因为我什至在执行 execvp 之前就将命令放入了历史记录中。
//A method which increments the histIndex
int incrementIndex(int index) {
if (index != 9)
return index+1;
else
return 0;
}
//A method which adds the current command to history
void updateHistory(char *history[], char command[], int histIndex) {
history[histIndex] = realloc(history[histIndex], MAX_LENGTH); //Allocating space
strcpy(history[histIndex], command); //Put the current command in history
}
int main(int argc, char *argv[]) {
//while true, do
while (1) {
int pid = fork(); //fork, or start the first process
if (pid != 0) { //if pid is not 0, then this is the parent,
wait(NULL);
}
else { //Otherwise, we have the child
printf("%s> ", getenv("USER")); //print out the user's name + >
fgets(input, MAX, stdin); //Get the users input
strtok(input, "\n"); //Take the entire line and set it to input
updateHistory(history, input, histIndex); //Updating history
histIndex = incrementIndex(histIndex);
if (strcmp(input, "exit")==0) { //If the input is exit, then exit
exit(0);
}
//Else if the current command is history, then print the last 10 commands
else if (strcmp(input, "history")==0) {
getHistory(history, histIndex);
}
//Otherwise, do
else {
numTokens = make_tokenlist(input, tokens);
tokens[numTokens] = NULL;
char cmd[MAX_LENGTH];
strcpy(cmd, tokens[0]);
execvp(cmd, tokens);
printf("Error: %s not found.\n", cmd);
}
}
}
}
单独的进程有自己独立的内存space(除非你做了一些特殊的事情,比如共享内存等)。您在 child 进程中对堆或堆栈结构所做的任何更新(例如修改历史记录)都不会影响 parent.
您正在使用 fork()
创建 child,然后读取 child 中的用户输入 。 child 更新它自己的历史副本,这对 parent 知道的任何历史都没有影响。 execvp()
不fork,它用执行的文件替换当前进程。这取代了整个 child 过程,并且您丢失了仅在 child.
中更新的历史记录
你也有 children 制作 children,这可能不是你想要的,但解释了为什么你认为它在历史记录中添加了无效的命令。 (是的,但不正确。)事件顺序的说明:
Parent
------
fork() ------> Child
wait() -----
... Read input
... updateHistory()
... exit if "exit"
... print history if "history"
... execvp()
(if execvp() succeeded, this child is consumed,
the executed file eventually terminates and the
parent stops waiting. If execvp() failed, we fall
through back to the top of the while loop!)
fork() ---------> Child's Child
wait() -------------
... Read input
... updateHistory()
... exit if "exit"
... print history if "history"
... execvp()
child的child继承了child的记忆,所以知道更新历史。这就是为什么您认为它将失败的命令添加到历史记录中的原因。是的,但实际上比那更糟糕。
您似乎应该在 parent 中读取输入,在 parent 中更新历史记录,然后(给定一个有效的命令),fork
关闭一个 child 进程,由 execvp
消耗到 运行 命令。然后让 parent wait
让 child 完成。这样,parent 维护了历史。首先分叉 child 的一个主要目的是因为 execvp
替换了调用进程。既然你想让 parent 活下去,你就让它吃掉 child.
尝试这样的事情(我将其保留为抽象伪代码):
Parent
------
Read input
updateHistory()
exit if "exit"
print history if "history"
if invalid, go back to [Read input]
if valid:
fork() ------> Child
wait() -----
... execvp()
... <------- if successful, executable hopefully terminates
... <------- if failed, print error and exit
(either way, child ends)
Parent goes back to [Read input]
另一件值得一提的事情是,每当你 fork()
时,你应该检查三个可能的 return 值:-1(fork()
中的错误),>0([=54 中的错误) =] 进程)和 0(在 child 进程中)。
我目前正在制作自己的 shell 程序。我必须保留用户输入的最后 10 个命令的历史记录。每当用户输入不是常规命令的命令(例如 history、hi、fakecommand 等)时,它就会被放入历史记录中。但是每当用户输入一个真正的命令(例如 ls、ps、top、cat 等)时,它都不会添加到历史记录中。
我认为这可能与 execvp 命令有关,因为我相信该命令会创建一个分支并让子进程执行该命令。但我不确定它是否应该这样做,因为我什至在执行 execvp 之前就将命令放入了历史记录中。
//A method which increments the histIndex
int incrementIndex(int index) {
if (index != 9)
return index+1;
else
return 0;
}
//A method which adds the current command to history
void updateHistory(char *history[], char command[], int histIndex) {
history[histIndex] = realloc(history[histIndex], MAX_LENGTH); //Allocating space
strcpy(history[histIndex], command); //Put the current command in history
}
int main(int argc, char *argv[]) {
//while true, do
while (1) {
int pid = fork(); //fork, or start the first process
if (pid != 0) { //if pid is not 0, then this is the parent,
wait(NULL);
}
else { //Otherwise, we have the child
printf("%s> ", getenv("USER")); //print out the user's name + >
fgets(input, MAX, stdin); //Get the users input
strtok(input, "\n"); //Take the entire line and set it to input
updateHistory(history, input, histIndex); //Updating history
histIndex = incrementIndex(histIndex);
if (strcmp(input, "exit")==0) { //If the input is exit, then exit
exit(0);
}
//Else if the current command is history, then print the last 10 commands
else if (strcmp(input, "history")==0) {
getHistory(history, histIndex);
}
//Otherwise, do
else {
numTokens = make_tokenlist(input, tokens);
tokens[numTokens] = NULL;
char cmd[MAX_LENGTH];
strcpy(cmd, tokens[0]);
execvp(cmd, tokens);
printf("Error: %s not found.\n", cmd);
}
}
}
}
单独的进程有自己独立的内存space(除非你做了一些特殊的事情,比如共享内存等)。您在 child 进程中对堆或堆栈结构所做的任何更新(例如修改历史记录)都不会影响 parent.
您正在使用 fork()
创建 child,然后读取 child 中的用户输入 。 child 更新它自己的历史副本,这对 parent 知道的任何历史都没有影响。 execvp()
不fork,它用执行的文件替换当前进程。这取代了整个 child 过程,并且您丢失了仅在 child.
你也有 children 制作 children,这可能不是你想要的,但解释了为什么你认为它在历史记录中添加了无效的命令。 (是的,但不正确。)事件顺序的说明:
Parent ------ fork() ------> Child wait() ----- ... Read input ... updateHistory() ... exit if "exit" ... print history if "history" ... execvp() (if execvp() succeeded, this child is consumed, the executed file eventually terminates and the parent stops waiting. If execvp() failed, we fall through back to the top of the while loop!) fork() ---------> Child's Child wait() ------------- ... Read input ... updateHistory() ... exit if "exit" ... print history if "history" ... execvp()
child的child继承了child的记忆,所以知道更新历史。这就是为什么您认为它将失败的命令添加到历史记录中的原因。是的,但实际上比那更糟糕。
您似乎应该在 parent 中读取输入,在 parent 中更新历史记录,然后(给定一个有效的命令),fork
关闭一个 child 进程,由 execvp
消耗到 运行 命令。然后让 parent wait
让 child 完成。这样,parent 维护了历史。首先分叉 child 的一个主要目的是因为 execvp
替换了调用进程。既然你想让 parent 活下去,你就让它吃掉 child.
尝试这样的事情(我将其保留为抽象伪代码):
Parent ------ Read input updateHistory() exit if "exit" print history if "history" if invalid, go back to [Read input] if valid: fork() ------> Child wait() ----- ... execvp() ... <------- if successful, executable hopefully terminates ... <------- if failed, print error and exit (either way, child ends) Parent goes back to [Read input]
另一件值得一提的事情是,每当你 fork()
时,你应该检查三个可能的 return 值:-1(fork()
中的错误),>0([=54 中的错误) =] 进程)和 0(在 child 进程中)。