我正在尝试制作一个简单的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()等的工作方式吗?据我所知,上面的代码应该做我想做的事。
请咨询,谢谢。
答案 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_inline
,malloc
使用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);
这是一种相当可怕的方法,因为你会泄漏内存,但它让我的观点得到了解决。