为什么这段代码没有被执行?

时间:2016-02-06 05:01:14

标签: c debugging exec parent-child pid

我目前正在制作自己的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);
        }
    }
}
}

1 个答案:

答案 0 :(得分:2)

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

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

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

    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()

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

您似乎应该在中读取输入,在中更新历史记录,然后(给出有效命令),fork关闭execvp用于运行命令的子进程。然后让孩子的父wait完成。这样,父母就会保留历史记录。首先分配孩子的一个主要目的是因为execvp取代了调用过程。既然你希望父母继续生活,你就让它吃掉一个孩子。

尝试这样的事情(我将它留作抽象伪代码):

    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(),都应该检查三个可能的返回值:-1(fork()中的错误),> 0(在父进程中)和0(在儿童过程)。