fork() 之后数组更改值

发布于 2024-12-19 21:26:36 字数 1337 浏览 2 评论 0 原文

我正在尝试制作一个简单的 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);
}

但是我遇到了一个我似乎找不到答案的问题。在执行方法中执行 fork() 后,输入的字符串数组似乎与分叉之前的值不同。如果我尝试在分叉之前打印出存储在输入中的字符串,一切似乎都按顺序进行,但在分叉输入之后不再包含它应该具有的字符串,因此 execvp() 无法正确执行。

我是否错过了一些东西或者我误解了 fork() 等的工作原理?据我所知,上面的代码应该做我想做的事情。

请大家给点建议,谢谢。

I'm trying to make a simple shell-replica (in C) for learning purposes. The section of code below shows where I'm at the moment:

#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);
}

I have however run into a problem I can't seem to find an answer to. After I do fork() in the execute-method, the array of strings, input, doesn't appear to have the same values as before forking. If I try to print out the strings stored in input before forking, everything seems to be in order but after forking input doesn't longer contain the strings it should have and therefore execvp() doesn't execute properly.

Is there something I have missed or have I misunderstood something about how fork() etc. works? From what I can tell the code above should do what I want it to do.

Please be advice, thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凉宸 2024-12-26 21:26:36

你的问题与分叉无关。

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

Your problem has nothing to do with forking.

char in[70]; is on the stack. check_input processes it, but after it returns, the next function call(s) will start to overwrite that memory location, and consequently, overwrite the tokens. Use malloc for memory allocation.

南七夏 2024-12-26 21:26:36

决不将指向局部变量的指针返回给函数的调用者。

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 返回且堆栈帧时,它和那些指针将不再有效被摧毁了。

保留指向任何不再存在的变量的指针将导致未定义的行为。正如您所注意到的,当堆栈上的该位置被重用于其他任何内容(其他变量、函数调用等)时,您的(无效)字符串将会变得混乱。

另一种方法是在 tok_inline 上使用 strdup,它使用 malloc 来复制字符串。

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

for 循环签名更改为:for(i=0; i

另外,请考虑使用 fgets(in, 70, stdin); 而不是 gets,后者是不安全的,如果输入的大小大于该值,可能会导致缓冲区溢出你的缓冲区。另请注意,换行符 '\n' 如果适合,则存储在缓冲区的末尾。

EDIT2:此外,传递给 execvp 的第二个参数 (argv) 是一个 NULL 终止的字符串数组,即。 {"ls", NULL }。为此:

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

另外,如果您决定使用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;
}

EDIT3: 对于有关内存泄漏的最终查询,是的,您应该释放内存。您不需要在子进程中这样做,因为当它调用 exec 时,它的进程将被新进程的进程替换。

但是,您应该释放父级分配的内存。由于我们已经 NULL 终止了数组,因此只需将以下内容添加到 executeelse 块中的任意位置即可:

while (*input)
    free(*input);

Never return a pointer to a local variable back to the caller of a function.

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

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

The return value of strtok is a pointer to a position inside of in and not a copy or anything like that. Since in is allocated on the stack and not dynamically using malloc, it and those pointers will no longer be valid when check_input returns and the stack frame is destroyed.

Keeping a pointer to any of these variables that no longer exist will result in undefined behaviour. As you have noticed, when that position on the stack is reused for anything else (other variables, function calls, etc), your (invalid) strings are going to be messed up.

An alternative is to use strdup on tok_inline, which uses malloc to make a duplicate of the string.

EDIT: You should also be checking the return value of strtok (ie. tok_inline) in the condition of your for loop, and terminating also if NULL is returned - meaning that there are no more tokens.

Change your for loop signature to: for(i=0; i < NUMBEROFARGUMENTS && tok_inline; i++).

Also, consider using fgets(in, 70, stdin); as opposed to gets, which is unsafe and can cause buffer overflows if the size of the input is greater than that of your buffer. Also note that the newline char '\n' is stored at the end of the buffer if it fits.

EDIT2: Also, the second argument (argv) passed to execvp is a NULL terminated array of strings, ie. {"ls", NULL }. In order to do that:

  • Add input[i] = NULL; after the for loop in check_input
  • Change char* input[NUMBEROFARGUMENTS]; to char* input[NUMBEROFARGUMENTS + 1]; so that you have 1 more element in the array for the NULL.

Also, if you decide to use fgets, you're going to need to remove the '\n' from the end of the buffer (if it exists). Your check_input function could then look something like:

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;
}

EDIT3: In relation to your final query about memory leaks, yes you should free up the memory. You don't need to in the child, since when it calls exec it's process will be replaced with that of the new process.

You should, however, free up memory allocated by the parent. Since we've NULL terminated our array, simply add the following to anywhere in the else block of execute:

while (*input)
    free(*input);
執念 2024-12-26 21:26:36

将输入从 gets() 复制到堆栈变量时要小心。这条线

 input[i] = tok_inline;

就是你的问题所在。您必须将 tok_inline 字符串复制到 fork 之后的位置。就目前情况而言,当您在父进程中执行 printf() 时,它可能仍然存在,但它不会在您的子进程中。

你必须做这样的事情(未验证):

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

这是一种相当糟糕的方法,因为你会泄漏内存,但它明白了我的观点。

Watch out with copying your input from gets() to a stack variable. The line

 input[i] = tok_inline;

Is where your problems lie. You must copy the tok_inline string to a place will be around after a fork. As it stands, it might happen to be still there when you do a printf() in your parent, but it wont be in your child process.

You have to do something like this (not verified):

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

This is a rather terribly way to do it, since you'll leak memory, but it gets my point across.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文