自己怎么建立微网站后台,宜宾长宁网站建设,微商产品展示网站源码,图片链接怎么生成需要云服务器等云产品来学习Linux的同学可以移步/–腾讯云–/官网#xff0c;轻量型云服务器低至112元/年#xff0c;优惠多多。#xff08;联系我有折扣哦#xff09; 文章目录 1. 主要思路2. 流程图3. 实现过程3.1 初步实现3.2 当前路径3.3 内建命令/外部命令3.4…需要云服务器等云产品来学习Linux的同学可以移步/–腾讯云–/官网轻量型云服务器低至112元/年优惠多多。联系我有折扣哦 文章目录 1. 主要思路2. 流程图3. 实现过程3.1 初步实现3.2 当前路径3.3 内建命令/外部命令3.4 Shell 的最终实现 1. 主要思路
一个Shell是一直在运行着的为了保证Shell本身的运行稳定所以每次在接收到指令的时候会派生子进程把子进程替换成要执行的指令执行完毕之后子进程被回收然后再次回到等待指令的时候。 主要步骤 输出命令提示符从终端获取命令行输入解析命令行输入信息创建子进程进程程序替换进程等待 2. 流程图
通过上述的分析我们可以画出以下的流程图 3. 实现过程
3.1 初步实现
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/types.h
#include sys/wait.h
#include assert.h
#include errno.h#define NUM 1024//输入缓冲区大小
#define OPT_NUM 64//命令参数最大个数
char lineCommand[NUM];//输入缓冲区
char* argv[OPT_NUM];
int main()
{while(1)//死循环因为Shell要一只运行着{//打印输出命令提示符printf([用户名主机名 当前路径]$ );fflush(stdout);//由于打印命令提示符的时候没有换行所以这里手动刷新缓冲区//获取输入char* str fgets(lineCommand, sizeof(lineCommand) - 1, stdin);//最后一个位置用于在极端情况下保证字符串内有\0assert(str);//判断合法性(void)str;lineCommand[strlen(lineCommand) - 1] \0;//消除行命令中的换行//命令解析字符串切割argv[0] strtok(lineCommand, );int i 1;while(argv[i] strtok(NULL, ));//使用字符串切割函数依次拿到每个参数//创建子进程pid_t id fork();if(id -1){perror(fork);exit(errno);}else if(id 0){//child//进程程序替换execvp(argv[0], argv);//执行到此处的时候证明进程替换错误perror(exec:);exit(errno);}else{//parent//进程等待int status 0;//退出状态pid_t ret waitpid(id, status, 0);//阻塞等待if(ret -1){perror(wait fail);exit(errno);}}}return 0;
}可以看到这里我们自己的Shell已经能够执行Linux的基本指令啦但是我们的ls好像是没有颜色的那么这里我们在Shell里面加上特判
if(argv[0] ! NULL strcmp(argv[0], ls) 0) //ls颜色显示
{argv[i] (char*)--colorauto;
}3.2 当前路径
我们知道在进程运行起来之后在/proc目录下会有一个内存级的目录存放进程的相关信息我们可以在这里找到我们运行的进程。 可以看到这里有两个路径。 exe代表可执行文件在磁盘中的路径cwdcurrent work directory当前进程的工作路径即我们常说的当前路径 当前路径可以更改吗 可以当前路径可以通过系统调用chdir更改 path想要更改的路径 return value调用成功返回0失败返回-1 可以看到cwd已经被更改。 在我们之前实现的Shell中如果想要使用cd命令更改当前路径似乎是做不到的 这是因为按照我们之前的思路每次都是派生子进程来执行命令那么子进程的工作目录会切换到我们指定的路径但是子进程执行完毕之后就被回收了我们在父进程看不见路径的切换。
所以有了上述chdir的前置知识那么就很好解决这个问题了在命令解析之后如果发现遇到了cd命令就不要派生子进程直接使用父进程执行系统调用chdir。 3.3 内建命令/外部命令
Linux下的命令分为两种内建命令和外部命令 内建命令是 shell 程序的一部分其功能实现在 bash 源代码中不需要派生子进程来执行也不需要借助外部程序文件来运行而是由 shell 进程本身内部的逻辑来完成外部命令则是通过创建子进程然后进行进程程序替换运行外部程序文件等方式来完成。 我们可以使用type命令来区分内建命令还是外部命令 注博主这里使用的是汉化过的云服务器所以结果是中文翻译上有一点差别
我们上面对cd命令的处理就是内建命令的一种myshell 遇到 cd 命令时由自己直接来改变进程工作目录处理完毕直接 continue而不会创建子进程。
同时我们发现 echo 命令也是一个内置命令这其实也很好的解释了 为什么 “echo $变量” 可以查看本地变量以及为什么 “echo $?” 可以获取最近一个进程的退出码 了 虽然本地变量只在当前进程有效但是使用 echo 查看本地变量时shell 并不会创建子进程而是直接在当前进程中查找自然可以找到本地变量 shell 可以通过进程等待的方式获取上一个子进程的退出状态然后将其保存在 ? 变量中当命令行输入 “echo $?” 时直接输出 ? 变量中的内容然后将 ? 置为0 (echo 正常退出的退出码)也不需要创建子进程。 那么我们可以为我们的Shell添加echo命令了。 3.4 Shell 的最终实现
最后经过一系列的优化我们最终得到了一个简易的Shell的demo这里附上源码
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/types.h
#include sys/wait.h
#include assert.h
#include errno.h#define NUM 1024//输入缓冲区大小
#define OPT_NUM 64//命令参数最大个数
char lineCommand[NUM];//输入缓冲区
char* argv[OPT_NUM];
int EXIT_CODE;
int main()
{while(1)//死循环因为Shell要一只运行着{//打印输出命令提示符printf([用户名主机名 当前路径]$ );fflush(stdout);//由于打印命令提示符的时候没有换行所以这里手动刷新缓冲区//获取输入char* str fgets(lineCommand, sizeof(lineCommand) - 1, stdin);//最后一个位置用于在极端情况下保证字符串内有\0assert(str);//判断合法性(void)str;lineCommand[strlen(lineCommand) - 1] \0;//消除行命令中的换行//命令解析字符串切割argv[0] strtok(lineCommand, );int i 1;if(argv[0] ! NULL strcmp(argv[0], ls) 0)//识别ls自动加上颜色选项{argv[i] (char*)--colorauto;}while(argv[i] strtok(NULL, ));//使用字符串切割函数依次拿到每个参数if(argv[0] ! NULL strcmp(argv[0], cd) 0){if(argv[1] ! NULL){chdir(argv[1]);}else{printf(no such file or directory\n);}continue;}if(argv[0] ! NULL strcmp(argv[0], echo) 0){if(strcmp(argv[1], $?) 0){printf(%d\n, EXIT_CODE);EXIT_CODE 0;}else{printf(%s\n, argv[1]);}continue;}//创建子进程pid_t id fork();if(id -1){perror(fork);exit(errno);}else if(id 0){//child//进程程序替换execvp(argv[0], argv);//执行到此处的时候证明进程替换错误perror(exec:);exit(errno);}else{//parent//进程等待int status 0;//退出状态pid_t ret waitpid(id, status, 0);//阻塞等待EXIT_CODE (status 8) 0xFF;if(ret -1){perror(wait fail);exit(errno);}}}return 0;
}本节完