一直在用命令解释器,但是对其中涉及的过程还不是很清楚,偶然看了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  如下

 

对于上面的模拟过程,比较重要的就是这几个函数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);
	}

}

运行结果如下:


Logo

更多推荐