首先,在开始本篇博文之前,我们先得了解什么是shell?
我们都知道用户是通过操作系统来对我们的硬件或者软件进行操作的,但是我们平时在使用计算机的时候并没有直接与操作系统进行交流,具体关于操作系统的管理软硬件的层次结构参考前面的文件:http://blog.csdn.net/chenkaixin_1024/article/details/70158065
而在Linux下我们会通常通过终端输入命令,但是你有没有想过为什么我们在终端输入命令,操作系统能够认识呢?这中间存在一个命令行解释器shell(也就是所谓的外壳程序)来对我们输入的命令进行解释,调用相应的系统调用接口,从而实现各种操作。而我们这里就要尝试着来模拟实现shell:
要模拟实现shell,我们首先得知道shell的工作原理:在我们打开终端的同时,我们也就将shell这个进程加载到了内存中了,而对于我们输入的每一条命令,它也就是一个个可执行程序,也就是我们的shell这个进程通过fork()这个系统调用生成的子进程,shell这个进程本身并不去执行我们的命令功能,而是让它的子进程去执行对应操作,而它自己则是等待子进程的返回,来回收子进程的资源。
所以这里就不得不提及一个问题:父进程如何等待子进程的返回?
这里介绍两个系统调用接口:wait()和waitpid()
1.wait()
函数原型:pid_t wait(int * status)
参数:status作为输出型参数,用于获取进程退出状态,当我们不关心进程退出状态的时候,这一参数可以设置为NULL
返回值:若成功等到子进程返回,则返回子进程的pid,否则返回-1
这里需要注意一点的是当我们的父进程执行到wait()这条语句的时候,若子进程还并未返回,则父进程则一直处于这条语句,也就是我们的阻塞式等待。
2.waitpid()
函数原型:pid_t waitpid(pid_t pid , int* status, int options)
参数:status这一参数与wait()一样,pid是表示正在等待的子进程pid(pid==-1,等待任一子进程;pid>0,等待指定的子进程;pid==0,等待组id与调用进程组id相同的子进程;pid<-1,等待组id等于pid的绝对值的任一子进程),options等于0表示阻塞式等待,设置为WNOHANG则为非阻塞式等待(通过循环来实现父进程不在这里一直等待,而是去执行别的操作,一段时间后回来再进行判断子进程是否退出,若退出则等待成功,也就可以跳出循环了)
返回值:同wait()
而对于我们上面模拟实现shell,两种都可以采用,但我们这里采用waitpid(),原因在于我们要等待指定的子进程,但因为父进程并没有其他操作需要执行所以这里我们就可以选择阻塞式等待(options==0)即可,而对于status我们也并不关心,所以这里就可以设置为NULL。
模拟实现shell,必然要和shell的功能尽量贴切,而对于shell有一项功能需要注意,就是我们输出重定向。
对于我们的重定向,无非就是对于我们的输出在显示器文件上的东西重新定位到我们的文件当中,所以这里只要对于文件描述符“1”进行关闭,而之后打开我们的文件即可(文件描述符“1”-----stdout(原本是输出到显示器文件))
实现代码如下:
/*************************************************************************
> File Name: Myshell.c
> Author: chenkaixin
> Mail: chenkaixin_1024@sina.com
> Created Time: Thu 18 May 2017 09:16:44 PM PDT
************************************************************************/
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
while(1){
pid_t pid=fork();
if(pid==0)
{
printf("[Myshell@localhost]$ ");
fflush(stdout);
char buf[1024];
ssize_t readnum=read(0,buf,sizeof(buf)-1);
if(readnum>0)
{
buf[readnum-1]=0;//处理'\n'
}
char* start=buf;
char* argv[32];
memset(argv,0,32);
argv[0]=start;
ssize_t index=1;
while(*start)
{
if(isspace(*start))
{
*start='\0';
start++;
if(*start=='>')
{
start++;
while(isspace(*start))
{
start++;
}
char filename[32];
int idx=0;
while(!isspace(*start)&&*start!='\0')
{
filename[idx]=*start;
idx++;
start++;
}
filename[idx]='\0';
close(1);
int fd=open((const char*)filename,O_WRONLY|O_CREAT,0644);
}
else
argv[index++]=start;
}
else
{
start++;
}
}
argv[index]=NULL;
execvp(argv[0],argv);
exit(1);
}
else
{
waitpid(pid,NULL,0);
}
}
return 0;
}