实践任务
- 使用fork,exec,wait实现mybash
- 写出伪代码,产品代码和测试代码
- 发表知识理解,实现过程和问题解决的博客(包含代码托管链接)
预备知识
- fork()
- 目的:创建一个与原来进程几乎完全相同的进程,但是假如两个进程也可以完成不同的请求
- 练习代码如下:
#include <unistd.h>
#include <stdio.h>
int main ()
{
pid_t pid; //fpid表示fork函数返回的值
int count=0;
pid=fork();
if (pid < 0)
printf("error in fork!");
else if (pid == 0) {
printf("i am the child process, my process id is %d/n",getpid());
count++;
}
else {
printf("i am the parent process, my process id is %d/n",getpid());
count++;
}
printf("统计结果是: %d/n",count);
return 0;
}
-
执行结果
-
注意点:第二个count值不会是2,因为count是一个独立变量,而不是父进程和子进程共用的
-
exec()
- 目的:提供一个在进程中启动另一程序执行的方法。它可根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段。执行完后,原调用进程的内容除了进程号外,其他均被新程序的内容替换
- 函数原型:
int execve(const char *filename, char *const argv[], char *const envp[]);
其中执行程序由filename决定,filename必须是一个二进制的可执行文件
-
wait()
- 目的:用在父进程中等待回收子进程的资源,而防止僵尸进程的产生
-
函数原型:
int waitpid(pid_t pid, int* statloc, int options);
任务分析
-
bash是一个交互型的应用程序,主要解析并代表用户执行一系列的命令。
-
在我的mybash中主要实现两个功能,分别是若用户输入的是Linux命令,则直接执行此命令;若用户输入的是一个可执行文件则运行此文件。
-
因此可得出初步的思路
主函数{
while(1) //不断循环执行命令
{
得到用户输入的命令;
调用子函数1解析此命令
}
}
子函数1{
调用子函数2对用户输入的字符串进行解析,根据空格区分各个参数,为之后调用execve函数做准备;
调用execve函数执行子进程;
}
- 具体实现代码如下,参考了课本P513-P526的内容
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <stdlib.h>
#include <string.h>
#define MAX 128
void eval (char *cmdline); //对用户输入的命令进行解析
int parseline (char *buf, char **argv);
int builtin_command(char **argv);
int main()
{
char cmdline[MAX];
while(1){
printf("> ");
fgets(cmdline,MAX,stdin);
if(feof(stdin))
{
printf("error");
exit(0);
}
eval(cmdline);
}
}
void eval(char *cmdline)
{
char *argv[MAX];
char buf[MAX];
int bg;
pid_t pid;
char *envp[]={0,NULL};
strcpy(buf,cmdline);
bg = parseline(buf,argv);//解析以空格分隔的命令行参数,填入argv数组中
if(argv[0]==NULL)
return;
if(!builtin_command(argv)) //调用此函数检查用户输入的命令是否为内置的Linux命令,是则执行并返回1,不是则返回0
{
if((pid=fork()) == 0)
{
if(execve(argv[0],argv,envp) < 0) {
printf("%s : Command not found.
",argv[0]);
exit(0);
}
}
if(!bg){
int status;
if(waitpid(-1,&status,0) < 0) //相当于调用wait函数
printf("waitfg: waitpid error!");
}
else
printf("%d %s",pid, cmdline);
return;
}
}
int builtin_command(char **argv)
{
if(!strcmp(argv[0], "quit"))
exit(0);
if(!strcmp(argv[0],"&"))
return 1;
return 0;
}
int parseline(char *buf,char **argv)//解析以空格分隔的命令行参数,并构造最终传给execve函数的argv向量
{
char *delim;
int argc;
int bg;
buf[strlen(buf)-1]=' ';
while(*buf && (*buf == ' '))
buf++;
argc=0;
while( (delim = strchr(buf,' '))){ //从字符串buf中寻找空格字符第一次出现的位置
argv[argc++] = buf;
*delim= ' ';
buf = delim + 1;
while(*buf && (*buf == ' '))
buf++;
}
argv[argc] = NULL;
if(argc == 0)
return 1;
if((bg=(*argv[argc-1] == '&')) != 0)
argv[--argc] = NULL;
return bg;
}
问题及解决
-
一开始在执行命令的时候,直接输入ls、ps命令发现没办法正常执行,但是执行一个可执行文件就没有问题
根据提示得知,是因为没有找到此命令,猜测应该是路径有问题,第一个命令行参数argv[0]应该给出完整的路径名,因此将命令改为/bin/ls、/bin/ps就没问题了 -
目前的程序在执行Linux命令时还是需要把完整路径名给打出来,还不能像终端那样直接打命令,在这一点上要是能改进就好了
-
运行结果如下