第一个实例重点说明fork和exec系统函数
该实例是一个交互式命令处理程序,它能完成Linux系统标准Shell的小部分功能,具体功能如下所述:
1.提交命令的参数最多为8个
2.可前,后台执行
3.一命令行中可同时拥有多个命令,彼此之间用分号隔开
实现程序的主流程如下:
for(;;)
{
output("mini_SH-->");
readcmd();
docommand();
}
下面给出源代码:
#include <sys/types.h> #include <unistd.h> #include <stdio.h> #define MAXARG 10 #define LINSIZ 80 #define CMDSIZ 8 extern char **environ; char *quit="quit.quit"; char cmdbuf[CMDSIZ][LINSIZ]; int cmdflag[CMDSIZ]; int main() { int i; for( ; ;) { printf("mini_SH-->"); for(i=7;i>=0;i--) { cmdflag[i]=0; /* */ cmdbuf[i][0]='\0'; } if(i=readcmd()) /* */ docommand(i); /* */ else printf("read command failed, try again!!!\n"); } } readcmd() { char c,*p; int i=0; p=cmdbuf[0]; while((c=getchar())!='\n') { if(c==';') { *p='\0'; if(++i==6) return(++i); p=cmdbuf[i]; } else if(c=='&') { cmdflag[i]=1; } else *p++=c; } *p='\0'; return(++i); } docommand(int i) { int j, stat, pid; char *argl[MAXARG], args[LINSIZ]; char c, *argsp, **arglp, *p; for(j=0;j<i;j++) { arglp=argl; argsp=args; p=cmdbuf[j]; while((c=*p++)!='\0') { while(c==' '|| c=='\t') c=*p++; if(c=='\0') { *argsp++='\0'; break; } *arglp++=argsp; while(c!=' '&&c!='\t'&&c!='\0') { *argsp++=c; c=*p; if(c) p++; } *argsp++='\0'; } *arglp=(char *)0; if(strcmp(argl[0],quit)==0) { printf("Bye Bye!\n"); exit(0); } if((pid=fork())==0) { if(cmdflag[j]) setpgrp(); execve(argl[0],argl,environ); printf("Returned from execve: %s\n",cmdbuf[i]); exit(10); } else { if(! cmdflag[j]) while(wait(&stat)!=pid); } } }
程序代码说明:
(1) 数据结构说明。该mini_SH定义了每条命令所能使用的最大参数个数为MAXARG,定义为10。每条命令的字符缓存数组由LINSIZ决定,最大为80,一次提交的命令个数由CMDSIZ决定,最多8个。字符指针quit存放退出命令字符串,它已经赋值为quit.quit,二维数组cmdbuf存放标准输入读到的字符串,而数组cmdflag决定该命令以何种方式执行,0为前台,1为后台。
(2)main函数。按主流程的设计思想实现,在无限循环for中,首先打印命令接收提示符mini_SH-->,将用户输入的命令字符串,通过函数readcmd得到并存储在cmdbuf二维数组中,readcmd返回一次提交的用分号隔开的命令个数。而函数docommand执行存放在cmdbuf中的命令。
(3)readcmd函数。将用户从标准输入提交的一行命令,按分号为界,分别存放命令缓冲区cmdbuf中,如果命令字符中有“&”符,将命令标志数组cmdflag的相应位置为1,每次提交命令时,该数组字段被清为0,并返回提交的命令个数。
(4)docommand函数。在for循环中,每次执行一条命令。用户提交的命令按顺序存放在数组cmdbuf中,while循环将命令执行的参数以空格或制表符为分界线,将字符型的指针数组argl分别指向相应的字符串,argl[0]指向该命令字符串,arg[1]是该命令的第一个参数,以此类推,最后一个参数为空指针。对于每一个参数增加一个空字符”\0“。如果命令字符串为定义的退出该命令字符串quit.quit,则调用exit系统函数退出执行,否则,调用fork生成子进程。如果设置后台标志位,则重新设置进程组号,使用带环境变量的系统调用execve执行用户提交用户执行的命令,如该命令以后台方式执行,则父进程不等待该命令执行完后就可执行新的命令。否则,父进程使用wait函数等待子进程执行暂停或终止。
该程序是一个完整的程序,编译后生成mini_sh命令,一下是在mini_sh命令控制下,用户提交命令的执行情况:
$ ./mini_sh
mini_SH-->/bin/date
2011年12月05日 星期一 23:37:19 PST
mini_SH-->/bin/who
Returned from execve:
mini_SH-->/bin/pwd;/bin/date
/home/lxy/test1/BruceZhang
2011年12月05日 星期一 23:37:19 PST
mini_SH-->quit.quit
Bye Bye!