#include "csapp.h"
#define MAXARGS 128
/*本案例学习fork()函数建立进程,以及execve()函数加载并运行程序*/
//eval函数用于解析命令行并执行相关命令
void eval(char *cmdline)
{
char *argv[MAXARGS];
char buf[MAXLINE];
int bg;
pid_t pid;
strcpy(buf,cmdline); //复制字符串
bg=parseline(buf,argv);//bg为1或0
if(argv[0]==NULL) //参数为空,返回主函数
return;
if(!builtin_command(argv)) { //返回0的情况
if((pid=fork())==0) { //新建子进程
if(execve(argv[0],argv,environ)<0) { //运行带有参数的可执行程序,若出现错误,则execve函数会返回负数,执行错误提示,并且退出该子进程
printf("%s:Command not found.\n",argv[0]);
exit(0);
}
}
if(!bg) { //当bg为0时,即输入命令不是空且结尾字符串不是&的情况
int status;
if(waitpid(pid,&status,0)<0) //若等待子进程终止或退出时发生了错误,则返回-1,并且执行以下错误提示
printf("waitfg:waitpid error\n");
}
else //当bg为1时,输出进程ID及命令行参数
printf("%d,%s",pid,cmdline);
}
return;
}
//builtin_command函数用于判断命令是否合法
//例如若命令行第一个字符串为quit,则表示正常退出父进程(主程序)
//若第一个字符串为&,则返回1,否则返回0
//可以认为&开头表示该行命令为注释行,不用执行
int builtin_command(char **argv)
{
if(!strcmp(argv[0],"quit")) //quit键表示退出整个程序
exit(0);
if(!strcmp(argv[0],"&")) //&开头则返回1
return 1;
return 0; //一般情况下返回0
}
//parseline函数用于读取命令行参数,并将参数返回至argv中
int parseline(char *buf,char **argv)
{
char *delim;
int argc;
int bg;
buf[strlen(buf)-1]=' '; //将最后一位'\0'置为空格
while(*buf&&(*buf==' ')) //跳过开头部分的所有空格
buf++;
argc=0; //用于计数(参数个数)
while((delim=strchr(buf,' '))) { //strchr函数用于寻找字符在字符串中首次出现的位置,返回该位置指针
argv[argc++]=buf;//将单个参数存入argv指针当中
*delim='\0';
buf=delim+1;//更新buf所指向的地址
while(*buf&&(*buf==' ')) //跳过空格
buf++;
}
argv[argc]=NULL;
if(argc==0) //若参数个数为0,则返回1
return 1;
if((bg=(*argv[argc-1]=='&'))!=0) //若参数命令行最后一个字符为&,将&置为NULL,并且返回1;否则返回0
argv[--argc]=NULL;
return bg;
}
/*主函数入口*/
int main()
{
char cmdline[MAXLINE];
while(1) {
printf("> ");
fgets(cmdline,MAXLINE,stdin); //从终端读取输入
if(feof(stdin))
exit(0);
eval(cmdline); //执行子程序
}
}
运行示例
> ./main1.o
acbc> quit