一直在用命令解释器,但是对其中涉及的过程还不是很清楚,偶然看了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 ",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. "); 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 ",exitstatus>>8,exitstatus&0xff); } // execvp(arglist[0],arglist); // printf("end and error. "); // exit(1); } char *make(char *buf) //用于存储用户输入的参数 { char *cp; buf[strlen(buf)-1]=' '; 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!=' ') //获取输入的命令 当输入为回车时 执行命令 { 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 如下
对于上面的模拟过程,比较重要的就是这几个函数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 ",getpid()); execlp("ls","ls","-al","/etc/passwd",NULL); perror("execlp error."); }else{ sleep(1); printf("this is parent process.pid=%d ",getpid()); printf("wait for child process. "); pid=wait(&status); printf("child process coming,pid=%d,status=%d ",pid,status); } }
运行结果如下: