通过两天时间的努力,自己做的myshell终于成功了,现在分享一下自己做的时候出现的问题以及需要的地方。刚开始做的时候,自己框架不知道怎么搭,而且有很多细节想不通,就迟迟没有开写。整整过了一个早上之后,还是想不通,没办法了,就自己开始写吧,不能浪费时间不是,就一个一个函数写吧。这个决定让我自己最后有了一次大改的动作。
在这里先介绍一下我的思路:
- 先将命令保存起来。
- 将命令解析出来,放到二维数组当中。
- 根据命令的不同,比如(cd,>),给出一个标记的值。
- vfork一个进程,因为这样,调用系统命令时候才能不将父进程关闭。
- 根据不同的标记值,进入不同的函数。
- 循环。
下面贴出我的代码:
#include <unistd.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <fcntl.h> #include <dirent.h> void find_command(char ** argv1) { DIR * dir; struct dirent * ptr; pid_t pid; // pid = vfork(); // if( pid == 0 ) { if((dir = opendir("/usr/bin")) == NULL ) { perror("opendir!\n"); exit(0); } while((ptr = readdir(dir)) != NULL ) { if(strcmp(ptr->d_name,argv1[0]) == 0 ) { execvp(argv1[0], argv1); exit(0); // if(execvp(argv1[0],argv1) < 0) // perror("error!\n"); // printf("\nwhye\n"); // exit(0); } } printf("\nok\n"); } int chose(int count,char ** argv1) { int flag = 5,i; if( strcmp( argv1[0],"cd") == 0 ) { flag = 1; } for( i = 0 ; i < count ; i++ ) { if( strcmp(argv1[i],">") == 0 ) flag = 2; if( strcmp(argv1[i],"|") == 0 ) flag = 3; } return flag; } void change_dir(char ** argv1) { char buf[100]; if(chdir(argv1[1]) < 0) { perror("chdir!\n"); } getcwd(buf,100); printf("%s\n",buf); } void outputdir(int count,char ** argv1) { int i,j,num; int fd; char * argv[10]; pid_t pid; /* for( i = 0 , j = 0 ; i < count ; i++ ) { if() } */ for( i = 0 , j = 0; i < count ; i++) { if(strcmp(argv1[i],">") == 0 ) { num = i+1; break; } argv[j] = argv1[i]; j++; } argv[j] = NULL; if((fd = open(argv1[num],O_EXCL | O_TRUNC | O_CREAT | O_RDWR , 0666)) < 0 ) { perror("error!\n"); } // pid = vfork(); // if( pid == 0 ) { dup2(fd , 1); if(execvp(argv1[0],argv) == -1) { exit(-1); } exit(0); // } } int main() { char argv[15][100],shell_order[100],*argv1[15]; int i,j,k,count = 0,flag,fd; DIR * dir; struct dirent * ptr; pid_t pid; while(1) { count = 0; printf("llh my_shell@:"); gets(shell_order); for( i = 0 , j = 0 , k = 0 ; shell_order[k] != '\0' ; k++ , j++ ) { if( shell_order[k] == ' ' ) { argv[i][j] = '\0'; i++; j = -1; count++; continue; } argv[i][j] = shell_order[k]; } argv[i][j] = '\0'; count++; // printf("%d\n\n",count); for( i = 0 ; i < count ; i++ ) { // puts(argv[i]); argv1[i] = argv[i]; } argv1[i] = NULL; printf("%d\n",count); flag = chose(count,argv1); printf("%d\t\n\n\n",flag); pid = vfork(); if(pid ==0 ) { switch(flag) { case 1:change_dir(argv1);break; case 2:outputdir(count,argv1);break; default:find_command(argv1);break; } exit(0); } // find_command(argv1); wait(NULL); } return EXIT_SUCCESS; }
这个只是简单的shell,因为管道,后台的机制还不了解,这些功能后续接着加上。
现在说说我写的时候出现的问题。
- 使用execvp函数时候,第二个参数的问题我以为只是把将要执行的程序名字敲上去就好了,就直接写的argv[0],结果不停报错,我当时很纳闷。最后查到了这个第二个参数必须是以NULL,结尾的字符指针的数组。最后不得不加一步,让二维数组的地址保存到指针数组中。
- 写输出重定向时候,老是提示找不到目录(因为我设定在/usr/bin中查找)。进入到该目录下,发现确实没有“>”,但我的指针数组里有指向“>”的指针,没办法,只好接着写个指针数组将“>”以及“>”这个之后的文件名的指针都屏蔽掉。这才可以执行了。
- 输出重定向时候,还有一个问题,就是打开一个新文件,文件中没办法写入数据。执行后总是报出一个“bad file descriptior”。尝试好多次,发现文件是创建了,就是写不进去,出于无奈,百度问度娘,别人给出的解答是,打开的文件没有读写的权限,我恍然大悟,将O_RDWR参数加上。
- 刚写完时候,我用cd命令想测一下,看是否可以切换目录,结果发现是不可以的,我就去/usr/bin目录下查找,可是这次却发现,该目录下却是有这个程序的,我猜想是/usr/bin是不能调用的,我就想着改个名字试一下,可改之后,在终端下还是可以用的。因此原因不在这里。我现在想着可能子进程切换目录了,但因为时间太短,就又回到原目录下了,我加上sleep函数来延长时间。可是发现也没用,程序的结果是根本就没有执行这个函数,直接退出了。现在想一下确实是这样,因为通过exec函数族打开新的进程之后,原进程直接结束了,所以没有执行sleep函数。那到底是什么问题呢?通过查书,发现父进程vfork的进程,跟父进程是共用的数据段,因此,父进程的环境变量,子进程是没法改变得,所以,cd是调用不了的。后面我就自己写了个cd的函数调用。
- 在最后排版时候发现,怎么也排版不了,因为我用的是vfork,所以当时完全对父子进程的顺序不担心,可是怎么调试,结果都跟fork的结果一样,先是父进程进入到了下一层循环,打印出了shell名字,然后是子进程执行的各种结果。调试好久,都没有得到想要的结果,是在没办法,用wait函数将父进程挂起来,等子进程结束。这一下成功了,翻书查看定义,发现vfork调用exec或exit之后,父进程可能被调度运行。错出在这里。
通过myshell的实现,才发现有这么多的问题需要注意,庆幸组长最后让我写这个程序了(原来没要求必须写),通过自己去思考,自己实现,发现其中有很多的欢乐。哈哈
LinuxC学习之myshell的实现
最新推荐文章于 2018-05-09 17:13:50 发布