什么是shell?
Linux系统的shell作为操作系统的外壳,为用户提供使用操作系统的接口。它是命令语言、命令解释程序及程序设计语言的统称。
shell如何运行程序?
shell用fork建⽴新进程,⽤execv函数簇在新进程中运⾏⽤户指 定的程序,最后shell⽤wait命令等待新进程结束。wait系统调 ⽤同时从内核取得退出状态或者信号序号以告知⼦进程是如何结束的。
编写思路:
①使用while(1)循环实现shell的重复使用
②实现一个接口(函数),功能是将输入的字符串以空格为分界分成多个可执行的字符串,再分别将分隔后的字符串的地址存储在字符指针数组内
eg:
字符串:ls -l
指针数组:char buf[8]//一般一次输入指令的长度不会超过8个
分隔后的存储:buf[0]="ls";buf[1]="-l";buf[3]=NULL;
//这里为什么要加一个buf[3]=NULL;,在下面的“execvp函数的介绍”中会有解释。
③实现一个接口(函数),功能是将已保存“指令字符串的指针”数组挨个读取,使用execvp进行替换实现“指令字符串”对应的指令功能,具体看下面“execvp函数的介绍”。
————————————————
原文链接:https://blog.csdn.net/w_y_x_y/article/details/80013586
execvp函数的介绍:
进程替换空间:将当前进程的用户空间代码和数据完全替换成新程序,但是进程ID并不会变
int execvp(const char* file,char* const argv[]);
//形参分别为(可执行程序名,main函数参数列表)
//第一个参数:可执行程序所在的绝对位置
//第二个参数:可执行程序运行时所需的参数,这里是一个可变参数,每个参数之间用逗号分割
//参数结束时,最后一个参数是:NULL
1_模拟shell(非书本上实验要求):
1、cd /
2、cd home 进入home目录下
3、mkdir testshell 创建一个testshell文件
cd testshell 进入文件夹
4、vim myshell.c
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<ctype.h>
#include<sys/wait.h>
void do_execvp(int argc,char* argv[])
{
if(fork() == 0)//子进程会进入if,在子进程中fork()返回零,在父进程中返回新创建子进程的进程id
{
execvp(argv[0],argv);//execvp会将正在运行的进程的内存替换成要执行的进程的内存
perror("execvp");//如果走到这步,说明execvp执行失败,因为如果执行成功,这句代码所在的内存已经被替换了,这句代码早就不在了
exit(0);//则perror报错并退出程序
}
wait(NULL);//加一个阻塞,等待子进程结束父进程再运行,防止父进程和子进程并发运行
}
void do_parse(char* buf)
{
char* argv[8]={};
int argc=0;
int i=0;
int status=0;
for(i=0;buf[i]!=0;i++)
{
if(status == 0 && !isspace(buf[i]))
{
argv[argc++]=buf+i;
status=1;
}
else if(isspace(buf[i]))//判断字符c是否为空白符,当c为空白符时,返回非零值,否则返回零
{
status=0;
buf[i]=0;
}
}
argv[argc]=NULL;
do_execvp(argc,argv);
}
int main()
{
char buf[1024];
while(1)
{
printf("my shell>>");
memset(buf,0,sizeof(buf));
while(scanf("%[^\n]%*c",buf) == 0)//读取失败返回零
{
while(getchar()!='\n');
printf("my shell>>");
}
//内置命令:不可以使用execvp执行
//eg:exit
if(strncmp(buf,"exit",4) == 0 )//若输入为exit则返回零,执行退出命令
{
exit(0);
}
do_parse(buf);
}
}
5、vim Makefile
myshell:myshell.c
gcc -o myshell myshell.c
.PHONY:clean
clean:
rm -f myshell
注意缩进!!!
6、make 编译
7、./myshell 执行文件
2_实现模拟shell(按照书上实验要求)
编写三个不同的程序cmd1.c、cmd2.c及cmd3.c,每个程序的功能自定,分别编译成可执行文件cmd1、cmd2、cmd3.然后再编写一个程序,模拟shell程序的功能:能根据用户输入的字符串(表示相应的命令名),为相应的命令创建子进程并让他去执行相应的程序,而父进程则等待子进程结束,然后再接收下一条命令。如果接收到的命令为exit,则父进程结束,退出模拟shell;如果接收到的命令是无效命令,则显示“Command not found”,继续等待输入下一条命令。
1、同上新建一个文件夹,进入文件夹目录下
2、编写test1.c
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void childprocess(int num,char * const argv[],char ** environ){
pid_t pid=fork();
if(pid<0){
printf("Create child process failed \n");
}
else if(pid==0){
switch(num){
case 1:execve("cmd1",argv,environ);
case 2:execve("cmd2",argv,environ);
case 3:execve("cmd3",argv,environ);
default:printf("process will never go here");
}
}
}
int main(int argc,char * const argv[], char **environ){
char a[10];
while(1){
scanf("%s",&a);
if (strcmp(a,"exit")==0) break;
else if (strcmp(a,"cmd1")==0){
childprocess(1,argv,environ);
}
else if (strcmp(a,"cmd2")==0){
childprocess(2,argv,environ);
}
else if (strcmp(a,"cmd3")==0){
childprocess(3,argv,environ);
}
else printf("Command not found\n");
}
}
3、编写cmd1、cmd2、cmd3
#include<stdio.h>
int main(){
printf("I'm cmd1\n");
}
#include<stdio.h>
int main(){
printf("I'm cmd2\n");
}
#include<stdio.h>
int main(){
printf("I'm cmd3\n");
}
4、编译四个文件
5、执行文件 ./test1