【问题标题】:Why does this code not get executed?为什么这段代码没有被执行?
【发布时间】:2016-05-16 05:16:56
【问题描述】:

我目前正在制作自己的 shell 程序。我必须保留用户输入的最后 10 个命令的历史记录。每当用户输入一个不是常规命令的命令(例如 history、hi、fakecommand 等)时,它就会被放置在历史记录中。但是每当用户输入一个真正的命令(例如 ls、ps、top、cat 等)时,它都不会添加到历史记录中。

我认为这可能与 execvp 命令有关,因为我相信该命令会创建一个 fork 并使子进程执行该命令。但我不确定它是否应该这样做,因为我什至在执行 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);
        }
    }
}
}

【问题讨论】:

  • updateHistory() 函数导致内存泄漏,因为它在进行新分配之前没有将history[] 中的任何现有内存分配指针传递给free()
  • 请发布incrementHistory()函数
  • 在此行之后:printf("Error: %s not found.\n", cmd) 插入行:exit(EXIT_FAILURE);,因为您不希望孩子表现得像父母一样。
  • @user 我添加了 incrementIndex 函数。而且我不确定您所说的内存泄漏是什么意思。你是说我必须在重新分配之前释放数据?
  • history[] 的更新正在子进程中执行。子进程拥有history[] 数组的副本,因此父进程不会看到更新。

标签: c debugging exec parent-child pid


【解决方案1】:

独立的进程有自己独立的内存空间(除非你做一些特殊的事情,比如共享内存等)。无论您在子进程中对堆或堆栈结构进行什么更新(例如修改历史记录),对父进程都没有影响。

您正在使用fork() 创建一个孩子,然后在孩子中读取用户输入。孩子更新自己的历史副本,这对父母知道的任何历史都没有影响。 execvp() 不分叉,它用执行的文件替换当前进程。这会替换整个子进程,并且您会丢失仅在子进程中更新的历史记录。

你也有孩子生孩子,这可能不是你想要的,但解释了为什么你认为它在历史中添加了无效的命令。 (正确,但不正确。)事件顺序的说明:

家长 ------ fork() ------> 孩子 等待() - - - ... 读取输入 ...更新历史() ...退出如果“退出” ...如果“历史”打印历史 ...执行vp() (如果 execvp() 成功,这个孩子被消耗, 执行的文件最终终止并且 父母停止等待。如果 execvp() 失败,我们会失败 回到while循环的顶部!) fork() ---------> 孩子的孩子 等待() - - - - - - - ... 读取输入 ...更新历史() ...退出如果“退出” ...如果“历史”打印历史 ...执行vp()

孩子的孩子继承了孩子的记忆,所以它知道更新的历史。这就是为什么您认为它将失败的命令添加到历史记录中的原因。确实如此,但实际上比这更糟糕。

您似乎应该在 parent 中读取输入,更新 parent 中的历史记录,然后(给定有效命令),fork 关闭子进程供execvp 使用以运行命令。然后让家长wait为孩子完成。这样,父母就可以维护历史。首先 fork 一个子进程的一个主要目的是因为execvp 替换了调用进程。既然你想让父母活下去,就让它吃掉一个孩子。

尝试这样的事情(我将其保留为抽象伪代码):

家长 ------ 读取输入 更新历史() 如果“退出”则退出 如果“历史”打印历史 如果无效,返回[读取输入] 如果有效: fork() ------> 孩子 等待() - - - ...执行vp() ...

另外值得一提的是,无论何时fork(),都应该检查三个可能的返回值:-1(fork() 中的错误)、>0(在父进程中)和 0(在子进程中)。

【讨论】:

  • 好的。所以没有添加有效命令的原因是因为 execvp 在历史记录更新之前用子进程的内存替换了我当前的进程?
  • execvp 替换了整个子进程。当您updateHistory() 时,您只是在更新孩子的记忆。然后你打电话给execvp,孩子就被消耗掉了。父母的历史不受影响,因为您从未更改过它。
  • 这是一个很好的解释。我真的非常感谢你向我解释这个!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-26
  • 2011-01-14
  • 1970-01-01
相关资源
最近更新 更多