fork()后数组更改值

时间:2011-12-06 22:10:22

标签: c arrays string exec fork

我正在尝试制作一个简单的shell副本(在C中)用于学习目的。下面的代码部分显示了我目前所处的位置:

#define TRUE ( 1 )
#define NUMBEROFARGUMENTS ( 5 )

void execute(char** input){

    pid_t pid;
    pid = fork();   

    if (pid == 0){
        printf("Spawned foreground process pid: %d\n", getpid());
        execvp(input[0], input);
        _exit(1);
    } else {
        waitpid(pid, NULL, 0);
        printf("Foreground process %d terminated.\n", pid);
    }

}

void check_input(char** input){

    char in[70];
    char *tok_inline;

    gets(in);   
    tok_inline = strtok(in," ");

    int i;
    for(i=0; i < NUMBEROFARGUMENTS; i++){
        input[i] = tok_inline;
        tok_inline = strtok(NULL," ");
    }

}

int main(){

    char* input[NUMBEROFARGUMENTS];

    printf("MiniShell v2.5\n");

    while ( TRUE ){

        printf("--> ");

        check_input(input);

        if ( strcmp ( input[0], "cd" ) == 0 ){
            chdir(input[1]);
        } else if ( strcmp( input[0], "exit" ) == 0 ){
            exit(0);
        } else {    
            execute(input);
        }

    }
        exit(0);
}
然而,我遇到了一个我似乎无法找到答案的问题。在execute-method中执行fork()之后,字符串数组input似乎没有与分叉前相同的值。如果我尝试在分叉之前打印出存储在输入中的字符串,那么一切似乎都是有序的,但是在分叉后输入不再包含它应该具有的字符串,因此execvp()不能正确执行。

有什么我错过了或者我误解了fork()等的工作方式吗?据我所知,上面的代码应该做我想做的事。

请咨询,谢谢。

3 个答案:

答案 0 :(得分:5)

你的问题与分叉无关。

char in[70];在堆栈中。 check_input处理它,但在它返回后,下一个函数调用将开始覆盖该内存位置,从而覆盖令牌。使用malloc进行内存分配。

答案 1 :(得分:1)

从不将指向本地变量的指针返回给函数的调用者。

char in[70];
/* ... */
tok_inline = strtok(in," ");

int i;
for(i=0; i < NUMBEROFARGUMENTS; i++){
    input[i] = tok_inline;
    /* ... */

strtok的返回值是指向in内部位置的指针,而不是副本或类似内容。由于in在堆栈上分配而不是动态使用malloc,因此当check_input返回并且堆栈帧被销毁时,它和那些指针将不再有效。

保持指向不再存在的任何变量的指针将导致未定义的行为。正如您所注意到的那样,当堆栈上的该位置被重用于其他任何东西(其他变量,函数调用等)时,您的(无效)字符串将被搞砸。

另一种方法是在strdup上使用tok_inlinemalloc使用strtok复制字符串。

编辑:您还应该在tok_inline循环的条件下检查for(即NULL)的返回值,并且也会终止如果返回for - 意味着没有更多的令牌。

for(i=0; i < NUMBEROFARGUMENTS && tok_inline; i++)循环签名更改为:fgets(in, 70, stdin);

另外,考虑使用gets而不是'\n',这是不安全的,如果输入的大小大于缓冲区的大小,可能会导致缓冲区溢出。另请注意,换行符char argv存储在缓冲区的末尾(如果适合)。

EDIT2:此外,传递给execvp的第二个参数(NULL)是一个{"ls", NULL }终止的字符串数组,即。 input[i] = NULL;。为了做到这一点:

  • for
  • 中的check_input循环后添加char* input[NUMBEROFARGUMENTS];
  • char* input[NUMBEROFARGUMENTS + 1];更改为NULL,以便在fgets的数组中再添加一个元素。

此外,如果您决定使用'\n',则需要从缓冲区末尾删除check_input(如果存在)。您的void check_input(char** input){ char in[70]; char *tok_inline; size_t len; fgets(in, 70, stdin); len = strlen(in); if (in[len - 1] == '\n') in[len - 1] = '\0'; tok_inline = strtok(in," "); int i; for(i=0; i < NUMBEROFARGUMENTS && tok_inline; i++){ input[i] = strdup(tok_inline); tok_inline = strtok(NULL," "); } input[i] = NULL; } 函数可能类似于:

exec

EDIT3:关于内存泄漏的最终查询,是的,你应该释放内存。你不需要在孩子身上,因为当它调用NULL时,它的过程将被替换为新过程的过程。

但是,您应该释放父级分配的内存。由于我们已else终止了我们的数组,只需将以下内容添加到execute while (*input) free(*input); 块中的任何位置:

{{1}}

答案 2 :(得分:0)

注意将输入从gets()复制到堆栈变量。这条线

 input[i] = tok_inline;

你的问题在哪里。你必须将tok_inline字符串复制到一个分叉后的地方。就目前而言,当您在父母中执行printf()时,它可能仍然存在,但它不会在您的子进程中。

你必须做这样的事情(未经核实):

input[i] = malloc((strlen(tok_inline)+1)*sizeof(*tok_inline));
strcpy(input[i], tok_inline);

这是一种相当可怕的方法,因为你会泄漏内存,但它让我的观点得到了解决。