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);
}
但是我遇到了一个我似乎找不到答案的问题。在执行方法中执行 fork() 后,输入的字符串数组似乎与分叉之前的值不同。如果我尝试在分叉之前打印出存储在输入中的字符串,一切似乎都按顺序进行,但在分叉输入之后不再包含它应该具有的字符串,因此 execvp() 无法正确执行。
我是否错过了一些东西或者我误解了 fork() 等的工作原理?据我所知,上面的代码应该做我想做的事情。
请大家给点建议,谢谢。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你的问题与分叉无关。
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. Usemalloc
for memory allocation.决不将指向局部变量的指针返回给函数的调用者。
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
函数可能类似于:EDIT3: 对于有关内存泄漏的最终查询,是的,您应该释放内存。您不需要在子进程中这样做,因为当它调用
exec
时,它的进程将被新进程的进程替换。但是,您应该释放父级分配的内存。由于我们已经
NULL
终止了数组,因此只需将以下内容添加到execute
的else
块中的任意位置即可:Never return a pointer to a local variable back to the caller of a function.
The return value of
strtok
is a pointer to a position inside ofin
and not a copy or anything like that. Sincein
is allocated on the stack and not dynamically usingmalloc
, it and those pointers will no longer be valid whencheck_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
ontok_inline
, which usesmalloc
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 yourfor
loop, and terminating also ifNULL
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 togets
, 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 toexecvp
is aNULL
terminated array of strings, ie.{"ls", NULL }
. In order to do that:input[i] = NULL;
after thefor
loop incheck_input
char* input[NUMBEROFARGUMENTS];
tochar* input[NUMBEROFARGUMENTS + 1];
so that you have 1 more element in the array for theNULL
.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). Yourcheck_input
function could then look something like: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 theelse
block ofexecute
:将输入从 gets() 复制到堆栈变量时要小心。这条线
就是你的问题所在。您必须将 tok_inline 字符串复制到 fork 之后的位置。就目前情况而言,当您在父进程中执行 printf() 时,它可能仍然存在,但它不会在您的子进程中。
你必须做这样的事情(未验证):
这是一种相当糟糕的方法,因为你会泄漏内存,但它明白了我的观点。
Watch out with copying your input from gets() to a stack variable. The line
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):
This is a rather terribly way to do it, since you'll leak memory, but it gets my point across.