基於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,如果不是就返回繼續執行父進程;

運行截圖

碼雲鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM