SHELL外壳的简易实现
1.什么是shell外壳?
shell外壳是连接用户和操作系统的一种中间层软件,负责将用户输入的指令经过处理后提交给操作系统。shell外壳本身也是一个进程,它最主要的职责就是创建子进程并让子进程完成某些任务,但有时shell外壳也会自己执行任务,在linux系统下shell外壳称为bash。
2.如何实现shell外壳
shell外壳的简易实现先依靠fork函数创建子进程,再利用exec*系列函数把子进程替换成某些已经存在于磁盘的可执行程序,最后利用循环实现子进程的持续创建和替换,而父进程shell则不受影响。
以下是shell外壳的模拟实现代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#define SUM 1024
#define SIZE 32
#define SEP " "
char cmd_line[SUM];
char* g_argv[SIZE];
int main()
{
while(1)
{
printf("[root]");
fflush(stdout);
memset(cmd_line,'\0',sizeof(cmd_line));
if(fgets(cmd_line,sizeof(cmd_line),stdin)==NULL)//从标准输入流获取字符串数据
{
continue;
}
cmd_line[strlen(cmd_line)-1]='\0';//把fgets最后输入的回车覆盖掉
g_argv[0]=strtok(cmd_line,SEP);
int index=1;
if(strcmp(g_argv[0],"ls")==0)//以下是两种特殊命令,单独处理
{
g_argv[index++]="--color=auto";
}
if(strcmp(g_argv[0],"ll")==0)
{
g_argv[0]="ls";
g_argv[index++]="-a";
g_argv[index++]="-l";
g_argv[index++]="--color=auto";
}
while(g_argv[index++]=strtok(NULL,SEP));//把strtok返回的多个指针存入指针数组中
if(strcmp(g_argv[0],"cd")==0)//cd命令不能由子进程进行,否则销毁后不影响父进程,应该由父进程进行
{
if(g_argv[1]!=NULL)
{
chdir(g_argv[1]);
}
continue;
}
pid_t id=fork();
if(id==0)
{
printf("指令执行中\n");
execvp(g_argv[0],g_argv);//进程替换为已经存在的环境变量指令
exit(-1);
}
int status=0;
pid_t ret=waitpid(id,&status,0);
printf("父进程标记\n");
printf("exit code:%d\n",WEXITSTATUS(status));//取得子进程退出码
}
}
总结模拟实现就是利用while循环实现循环往复执行,父进程永不退出,就像一个真正的shell一样,再利用字符数组存储输入命令,利用strtok函数分割字符串存入指针数组中,再交由子进程的exec函数取指定元素后进行替换,完成任务后子进程退出,父进程再次创建新的子进程。
附录:
为什么说环境变量是全局的,被子进程继承的?
//myproc.c
const char* const _env[1024]={"NUMBER=100","NULL"};
int main()
{
execle("./mytmp","mytmp",NULL,_env);
return 0;
}
//mytmp.c
int main()
{
printf("%s",getenv("NUMBER"));
return 0;
}
这里我们自定义的环境变量-env通过exec函数传递到子进程中,并由子进程利用getenv接收,假如这里传递的是myproc的main函数中的env参数,就可以实现环境变量在父子进程之间的传递了!