linux之编写命令解释器

时间:2022-01-31 15:54:41

       一直在用命令解释器,但是对其中涉及的过程还不是很清楚,偶然看了Understanding Unix/Linux Programming一书,对其原理有所了解,记录学习过程。

命令解释器主要干了这么三件事:

      1.接收用户输入命令

      2.创建一个子线程执行用户输入的命令

     3.父线程等待子线程执行完毕,继续接受用户命令

下面是一个模拟命令解释器的过程,详情参见下面代码注释:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>


#define MAXVARS 10 //用于接受参数的个数
#define ARGLEN 10 //用于接受一个参数所包含的字符个数



void execute(char *arglist[]) //用于执行命令的函数
{
int i,pid,exitstatus;
/*for(i=0;arglist[i]!=NULL;i++)
{
printf("%s\n",arglist[i]);
}
*/
pid=fork(); //在父线程中产生一个子进程 并将子线程的线程id返回给父线程的pid变量
switch(pid) //根据pid来判断 是执行父线程代码 还是子线程的代码
//因为fork()函数给父线程返回子线程的线程id 给自己返回0
{
case -1: //表示fork创建子线程失败
perror("fork faield");
exit(1); //结束父线程
case 0: //表示成功创建子线程 并且在子线程中执行输入相关的命令
sleep(3); //子线程休眠3s中
printf("child sleep 3 seconds over.\n");
execvp(arglist[0],arglist); //在PATH的环境变量里找到arglist[0]的命令 并执行
perror("execvp failed"); //如果上面的execvp正常执行的话 下面两句都不会执行 因为execvp会用arglist[0]命令的代码段和数据段替换掉子线程中的代码段和数据段
exit(1);
default: //在父线程中所作的操作 使用wait等待子线程的执行完成 并带回子线程中所传递回来的值的地址给exitstatus变量 而wait函数返回子线程的进程id
while(wait(&exitstatus)!=pid); //判断子线程是否执行完毕
printf("child exit with status %d,%d\n",exitstatus>>8,exitstatus&0xff);
}
//execvp(arglist[0],arglist);
//printf("end and error.\n");
//exit(1);
}

char *make(char *buf) //用于存储用户输入的参数
{
char *cp;
buf[strlen(buf)-1]='\0';
cp=malloc(strlen(buf));
strcpy(cp,buf);
return cp;
}


int main(void)
{
char *arglist[MAXVARS]; //用于接受用户输入的参数
int numargs; //用于记录用户输入了几个参数
char argbuf[ARGLEN]; //用于存储用户每一次输入的参数
//void execute(char *arglist[]);
//signal(SIGQUIT,SIG_IGN);
numargs=0;
while(numargs<MAXVARS) //不断的读取用户输入的命令并执行
{
printf("arg[%d]=",numargs); //显示提示信息
if(fgets(argbuf,ARGLEN,stdin)&&*argbuf!='\n') //获取输入的命令 当输入为回车时 执行命令
{
arglist[numargs++]=make(argbuf);
}
else{
if(numargs>0)
arglist[numargs]=NULL; //参数列表的最后一个参数必须为(char *)NULL
execute(arglist); //执行参数
numargs=0;
}
}
return 0;
}


 

2.使用 gcc execvpfromstdin2.c -o efs

3.运行 ./efs  如下

linux之编写命令解释器

 

对于上面的模拟过程,比较重要的就是这几个函数fork,execvp,exit,wait函数

     fork函数-----复制父进程的代码和数据,还有父进程所执行的位置(与父进程拥有相同的数据副本),并且返回两个值,一个值给父进程(即子线程的线程id),一个值给自己(为0),所以可通过fork的返回值来进行判断是位于子线程还是父线程

    execvp----使用一个新进程映像来替换当前进程映像(当前进程的代码和数据都将会被新进程替换)

    exit----结束进程,会关闭进程打开的文件,释放掉使用的内存,等等

    wait----会阻塞父线程,等待子线程执行完毕,并带回子线程的返回值.返回值为一个16位的数字,高8位为exit返回的数字.

 

 下面还有一个fork、execlp、wait函数使用的小例子:

#include <stdio.h>
#include <unistd.h>

#define LEN 10

main()
{
char command[LEN];
int pid;
int status;
if(fork()==0)
{
printf("this is a child process.pid=%d\n",getpid());
execlp("ls","ls","-al","/etc/passwd",NULL);
perror("execlp error.");
}else{
sleep(1);
printf("this is parent process.pid=%d\n",getpid());
printf("wait for child process.\n");
pid=wait(&status);
printf("child process coming,pid=%d,status=%d\n",pid,status);
}

}

运行结果如下:

linux之编写命令解释器