基于fork(),execvp()和wait()实现类linux下的bash——mybash

基于fork(),execvp()和wait()实现类linux下的bash——mybash

预备知识

  • fork():fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事http://blog.csdn.net/jason314/article/details/5640969
    • 重点是后一句话,如果初始参数或者传入变量不同,两个进程也可以做不同的事,意思就是虽然父进利用fork()函数创造了一个和自己完全一致的子进程,但由于子进程执行指针开始至位于fork()函数后,意思就是子进程不会再执行一次fork()上面的代码,所有的fork()前定义的变量,都将保持初始化的值。
  • wait():进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止,wait其实比较好理解http://blog.sina.com.cn/s/blog_759803690101aqeq.html
  • execvp():exec系统调用会从当前进程中把当前程序的机器指令清除,然后在空的进程中载入调用时指定的程序代码,最后运行这个新的程序http://www.linuxidc.com/Linux/2011-10/44527.htm.
    • 这样的定义就意味着,所有execvp()后面的代码都将不被执行,相当于在主函数里“重写”了一遍传入execvp函数中的程序,又在紧接着在后面加了句exit(1);这样往往带来不便,但根据定义,我们可以将fork和execvp结合,从而保护父进程。

产品伪代码

Step1:读入用户输入的指令;
Step2:调用fork函数生成一个子进程,并将fork返回的pid值赋给fpid;
Step3:调用wait函数,传入null;
Step4:判断fpid是否为零,如果为零执行Step5;如果不为零,执行Step6;
Step5:调用execvp函数,并把用户输入的指令传进去;
Step6:返回Step1;

产品代码

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include    <unistd.h>
#include    <sys/types.h>
#include    <sys/wait.h>

#define	MAXARGS		20				
#define	ARGLEN		100				

int execute( char *arglist[] )
{
	execvp(arglist[0], arglist);		
	perror("execvp failed");
	exit(1);
}

char * makestring( char *buf )
{
	char	*cp;

	buf[strlen(buf)-1] = '\0';		
	cp = malloc( strlen(buf)+1 );		
	if ( cp == NULL ){			
		fprintf(stderr,"no memory\n");
		exit(1);
	}
	strcpy(cp, buf);		
	return cp;			
}
int mybash(char *arglist[])
{
	
	int flag=0;
	flag=fork();
	wait(NULL);
	if(flag==0)	
	execute( arglist );
else return 1;
}




测试代码

#include<stdio.h>
#include	<string.h>
#include"head.h"
int mybash(char *arglist[]);
int test1()
{
char *test1[10],*test2[10],*test3[10],*test4[10],*test5[10],*test6[10];
test1[0]="ls";
test1[1]="-l";
test1[2]=0;

test2[0]="od";
test2[1]="-tc";
test2[2]="-tx1";
test2[3]="12.txt";
test2[4]=0;

test3[0]="mkdir";
test3[1]="success";
test3[2]=0;

test4[0]="git";
test4[1]="add";
test4[2]=".";
test4[3]=0;

test5[0]="git";
test5[1]="commit";
test5[2]="-m";
test5[3]="\"test11\"";
test5[4]=0;

test6[0]="git";
test6[1]="push";
test6[2]="origin";
test6[3]="master";
test6[4]=0;

int flag=0;
if(flag=mybash(test1)==1)printf("\n%s %s test Success!\n",test1[0],test1[1]);

flag=0;
if(flag=mybash(test2)==1)printf("\n%s %s %s %s test Success!\n",test2[0],test2[1],test2[2],test2[3]);

flag=0;
if(flag=mybash(test3)==1)printf("\n%s %s test Success!\n",test3[0],test3[1]);

flag=0;
if(flag=mybash(test4)==1)printf("\n%s %s %s test Success!\n",test4[0],test4[1],test4[2]);

flag=0;
if(flag=mybash(test5)==1)printf("\n%s %s %s %s test Success!\n",test5[0],test5[1],test5[2],test5[3]);

flag=0;
if(flag=mybash(test6)==1)printf("\n%s %s %s %s test Success!\n",test6[0],test6[1],test6[2],test6[3]);

return 0;
}
  • 测试运行截图

问题及解决方法

  • 问题1:因为使用的是execvp函数是放在主函数里的,往往都会直接终结掉父进程,这是主要问题;
  • 问题1解决:调用fork函数生成一个子进程,并且只允许execvp运行在子进程中,这样execvp终结掉的就只是子进程,而不会影响父进程,而对于fork函数完整复制父进程的子进程也会因为调用了execvp而及时终结掉,不会导致一个无谓的循环。
  • 问题2:怎么实现只让execvp运行在子进程,而不去影响父进程
  • 问题2解决:这是根本问题,解决了才能使得mybash正常的去运行去循环,因为fork函数的特性就是完整复制父进程,但子进程永远都是从fork后面执行意思就是,fork前面的变量将保持初始化的值,而不受fork前面的代码影响,所以,这里可以使用fpid来作为flag判断这是一个子进程还是一个父进程,如果是一个子进程那么就运行execvp,如果不是就返回继续执行父进程;

运行截图

码云链接

posted on 2017-10-20 12:16  20155213陆忠民  阅读(5214)  评论(4编辑  收藏  举报