我正在尝试用C语言编写一个shell,但是遇到了一个问题。shell应该在一个循环中运行,每次都会提示用户读取和解析来自stdin的文本。然后将参数划分为标记,并将每个标记放入参数向量中。然后,代码派生一个子代,然后使用参数向量作为参数运行命令。然后,代码等待该子进程终止,并打印关于该子进程的统计信息(运行时等)。问题是,每当我们运行程序时,当我们输入ls /home (例如)时,它不会列出主目录,而是列出我们当前所在的目录。此外,如果我们试图向代码中添加一个新变量,代码就会停止工作。你知道怎么解决这个问题吗?
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <string.h>
#define TRUE 1
#define BUFFERSIZE 129
int main (int argc, char* argv[]){
int status;
int who = RUSAGE_CHILDREN;
struct rusage usage;
struct rusage before_usage;
struct timeval start, end;
while(TRUE){
printf("==>: ");
char input_string[BUFFERSIZE];
memset(input_string, '\0', BUFFERSIZE);
fgets(input_string, BUFFERSIZE-1, stdin);
int i = 0;
char* input_arguments[32]; //Pointers to what the commands will be
char* token; //The specific piece of the input "ls" or "/home"
char* program;
int run_exec = 1; //To say whether execvp will run
token = strtok(input_string, " ");
char* prog = malloc(strlen(token)+1);
memset(prog, '\0', strlen(token));
strncpy(prog, token, strlen(token));
program = prog;
printf("PROGRAM: %s\n", prog);
token = strtok(NULL, " ");
if(strncmp(program, "exit", 4) ==0)
exit(0);
while(token != NULL && i < 32){
printf("TOKEN: %s \n", token);
char* tmp = malloc(strlen(token)+1);
memset(tmp, '\0', strlen(token)+1);
strncpy(tmp, token, strlen(token));
input_arguments[i]=tmp;
printf("INPUT: %s \n", input_arguments[i]);
if(strcmp(program, "cd") == 0){
chdir(input_arguments[0]);
run_exec = 0;
printf("EXEC: %d\n", run_exec);
}
printf("%d\n", i);
i++;
size_t tmp2 = 50;
printf("%s\n", getcwd(tmp, tmp2));
token = strtok(NULL, " ");
printf("IN LOOP\n");
}
input_arguments[i] = NULL;
printf("AFTER LOOP\n");
if(fork() != 0){
int start_time = gettimeofday(&start, NULL);
if(run_exec == 1){
getrusage(who, &before_usage);
waitpid(-1, &status, 0);
int end_time = gettimeofday(&end, NULL);
double wall_time_passed = (end.tv_sec -start.tv_sec)*1000
+ (end.tv_usec - start.tv_usec)/1000;
getrusage(who, &usage);
double user_time = (usage.ru_utime.tv_sec*1000 +
usage.ru_utime.tv_usec/1000);
double system_time = (usage.ru_stime.tv_sec*1000
+ usage.ru_stime.tv_usec/1000);
//long page_faults = 0;
//long soft_faults = 0;
//long invol = 0;
//long vol = 0;
printf("Number of Page Faults: %ld \n", usage.ru_majflt -before_usage.ru_majflt);
//printf("soft_faults: %lu\n", soft_faults);
printf("Number of Page Reclaims: %ld \n", usage.ru_minflt - before_usage.ru_minflt);
//printf("soft_faults: %lu\n", soft_faults);
printf("Number of times preempted involuntarily: %ld \n", usage.ru_nivcsw - before_usage.ru_nivcsw);
printf("Number of times preempted Voluntarily: %ld \n", usage.ru_nvcsw - before_usage.ru_nivcsw);
printf("User Time: %f \n", user_time);
printf("System Time: %f \n", system_time);
printf("Wall-Time: %f \n", (wall_time_passed));
}
}
else{
if(run_exec == 1){
printf("RUNNING EXEC: %s, %s \n", program, *input_arguments);
printf("EXEC: \n", execvp(program, input_arguments));
}
}
}
return 0;}
发布于 2013-09-11 06:08:21
尝试根据以下示例重新格式化您的变量:
....
if(run_exec == 1){
//printf("RUNNING EXEC: %s, %s \n", program, *input_arguments);
//printf("EXEC: \n", execvp(program, input_arguments));
char* args[] = { "ls", "/home" };
execvp(args[0], args);
}
.....您的代码已被注释,并替换为简单的数组"ls“、"/home”
或者,更具体地说,您必须将input_arguments设置为程序名,并开始从input_arguments添加程序参数。例如:
input_arguments[0] = "ls"
input_arguments[1] = "/var/log/"下面是你的代码的补丁:
27c27
< int i = 0;
---
> int i = 1;
38a39
> input_arguments[0]=prog;
106a108
> 发布于 2013-09-11 06:41:44
这里有几件事:
当作为该调用的结果执行C语言程序时,应将其作为C语言函数调用进入,如下所示:
int main (int argc,char *argv[]);
后来,它继续说:
arg0表示的参数,...是指向以null结尾的字符串的指针。这些字符串应构成新过程映像可用的参数列表。该列表由null指针终止。参数arg0应指向与某个exec函数通过启动的进程相关联的文件名。
这显然是你错过的要点。argv参数的第一个实参必须是作为path实参传递的相同值(或其允许的变体)。
更重要的是,通常shell可能会探索PATH变量本身,并通过execve调用替换进程映像(最后一个e是因为shell通常允许您修改环境变量),并且它们将在PATH中找到文件的实际路径和用户输入的argv参数作为*path传递。
关于这一点,POSIX标准规定
对严格一致的POSIX应用程序的要求还规定,作为第一个参数传递的值是与正在启动的进程相关联的文件名。..。在某些情况下,传递的文件名不是文件的实际文件名...
所以,如果你有一个像这样的可执行文件:
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("I was called as %s\n", argv[0]); return 0;
}您可以将其编译为test,但将其符号链接到another_test。
当以test身份运行时,它应该显示I was called as test,但当以another_test身份运行时则不会。这是有道理的,因为程序可能需要知道它们是如何被调用的。例如,busybox需要这样做才能知道用户想要什么。
但你发布的代码不仅仅是添加一些input_arguments[0] = program并在1中启动"int“,我上面放的那些东西也是正确的:不要重复发明轮子,strdup已经在那里了,你应该使用malloc+memset+strncpy而不是strdup。它的三个函数调用更少,代码的作用也更简洁。
每次循环结束时,它都会产生一个子程序,它并不关心程序是否没有产生(可能程序并不存在)。为什么不在检查完run_exec之后再创建分支呢?为什么不等待生成的特定线程(()在父线程上返回的pid_t )?
如果子进程不能生成镜像,为什么不干掉它呢?目前,如果子进程在execvp调用中失败,它将请求更多命令,一旦被杀死,父进程将打印其统计信息并继续执行作业。这可能是意想不到的,而且肯定是违反直觉的。
此外,我不知道你是否注意到了,但是你没有在标准输入中检查EOF,这可能会杀死你,如果你是从文件输入的,而不是交互的(或者如果用户输入了EOF Ctrl.+D)。您可能也应该将退出操作绑定到EOF,否则您将永远无法完成“使用”脚本文件。
https://stackoverflow.com/questions/18729369
复制相似问题