第一个实例重点说明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!