当前位置: 首页 > news >正文

7.进程控制(三)

一.上集回顾

建议先学上篇博客,再向下学习,上篇博客的链接如下:

https://blog.csdn.net/weixin_60668256/article/details/154126973?fromshare=blogdetail&sharetype=blogdetail&sharerId=154126973&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link

二.实现一个简易的shell

1.框架的实现

我们先将大框架搭出来

#include <iostream>
#include <cstdio>using namespace std;int main()
{while(true){PrintCommandLine(); //1.命令行提示符GetCommandLine();   //2.获取用户输入ParseCommandLine(); //3.分析命令ExecuteCommand();   //4.执行命令}return 0;
}

2.命令行提示符的实现

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>using namespace std;const int basesize = 1024;string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}string GetPwd()
{string pwd = getenv("PWD");return pwd.empty() ? "None" : pwd;
}string MakeCommandLine()
{// [ltw@alicloud myshell]$char command_line[basesize];snprintf(command_line,basesize,"[%s@ %s]# ",\GetUserName().c_str(),GetPwd().c_str());return command_line;
}void PrintCommandLine()
{printf("%s",MakeCommandLine().c_str());fflush(stdout);
}int main()
{while(true){PrintCommandLine(); //1.命令行提示符printf("\n");sleep(1);//GetCommandLine();   //2.获取用户输入//ParseCommandLine(); //3.分析命令//ExecuteCommand();   //4.执行命令}return 0;
}

3.获取用户对应输入

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>using namespace std;const int basesize = 1024;string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}string GetPwd()
{string pwd = getenv("PWD");return pwd.empty() ? "None" : pwd;
}string MakeCommandLine()
{// [ltw@alicloud myshell]$char command_line[basesize];snprintf(command_line,basesize,"[%s@ %s]# ",\GetUserName().c_str(),GetPwd().c_str());return command_line;
}void PrintCommandLine()
{printf("%s",MakeCommandLine().c_str());fflush(stdout);
}bool GetCommandLine(char command_buffer[],int size)
{//我们认为:我们要将用户输入的命令行,当做一个完整的字符串//"ls -a -l -n"char* result = fgets(command_buffer,size,stdin);if(!result){return false;}command_buffer[strlen(command_buffer) - 1] = 0;return true;
}int main()
{char command_buffer[basesize];while(true){PrintCommandLine(); //1.命令行提示符if(!GetCommandLine(command_buffer,basesize))   //2.获取用户输入{continue;}printf("%s\n",command_buffer);//ParseCommandLine(); //3.分析命令//ExecuteCommand();   //4.执行命令}return 0;
}

4.分析命令

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>using namespace std;const int basesize = 1024;
const int argvnum = 64;
char* gargv[argvnum];
int gargc = 0;string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}string GetPwd()
{string pwd = getenv("PWD");return pwd.empty() ? "None" : pwd;
}string MakeCommandLine()
{// [ltw@alicloud myshell]$char command_line[basesize];snprintf(command_line,basesize,"[%s@ %s]# ",\GetUserName().c_str(),GetPwd().c_str());return command_line;
}void PrintCommandLine()
{printf("%s",MakeCommandLine().c_str());fflush(stdout);
}bool GetCommandLine(char command_buffer[],int size)
{//我们认为:我们要将用户输入的命令行,当做一个完整的字符串//"ls -a -l -n"char* result = fgets(command_buffer,size,stdin);if(!result){return false;}command_buffer[strlen(command_buffer) - 1] = 0;if(strlen(command_buffer) == 0){return false;}return true;
}void ParseCommandLine(char command_buffer[],int len) //3.分析命令
{memset(gargv,0,sizeof(gargv));gargc = 0;// "ls -a -l -n"const char *sep = " ";gargv[gargc++] = strtok(command_buffer,sep);//这里直接就能写完while(gargv[gargc++] = strtok(nullptr,sep));gargc--;
}void debug()
{printf("argc: %d\n",gargc);for(int i = 0;gargv[i];i++){printf("argv[%d]: %s\n",i,gargv[i]);}
}int main()
{char command_buffer[basesize];while(true){PrintCommandLine(); //1.命令行提示符if(!GetCommandLine(command_buffer,basesize))   //2.获取用户输入{continue;}printf("%s\n",command_buffer);ParseCommandLine(command_buffer,strlen(command_buffer)); //3.分debug();//ExecuteCommand();   //4.执行命令}return 0;
}

5.执行命令

我执行命令不能直接拿我们的主进程进行执行,我们要创建子进程来处理我们的任务

exec*的接口我们要选择哪一个呢?

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;const int basesize = 1024;
const int argvnum = 64;
char* gargv[argvnum];
int gargc = 0;string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}string GetPwd()
{string pwd = getenv("PWD");return pwd.empty() ? "None" : pwd;
}string MakeCommandLine()
{// [ltw@alicloud myshell]$char command_line[basesize];snprintf(command_line,basesize,"[%s@ %s]# ",\GetUserName().c_str(),GetPwd().c_str());return command_line;
}void PrintCommandLine()
{printf("%s",MakeCommandLine().c_str());fflush(stdout);
}bool GetCommandLine(char command_buffer[],int size)
{//我们认为:我们要将用户输入的命令行,当做一个完整的字符串//"ls -a -l -n"char* result = fgets(command_buffer,size,stdin);if(!result){return false;}command_buffer[strlen(command_buffer) - 1] = 0;if(strlen(command_buffer) == 0){return false;}return true;
}void ParseCommandLine(char command_buffer[],int len) //3.分析命令
{memset(gargv,0,sizeof(gargv));gargc = 0;// "ls -a -l -n"const char *sep = " ";gargv[gargc++] = strtok(command_buffer,sep);//这里直接就能写完while(gargv[gargc++] = strtok(nullptr,sep));gargc--;
}void debug()
{printf("argc: %d\n",gargc);for(int i = 0;gargv[i];i++){printf("argv[%d]: %s\n",i,gargv[i]);}
}bool ExecuteCommand()   //4.执行命令
{//子进程执行命令pid_t id = fork();if(id < 0){return false;}else if(id == 0){//子进程//1.执行命令execvp(gargv[0],gargv);//2.退出exit(1);}int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){// Do Nothingreturn true;}return false;
}int main()
{char command_buffer[basesize];while(true){PrintCommandLine(); //1.命令行提示符if(!GetCommandLine(command_buffer,basesize))   //2.获取用户输入{continue;}//printf("%s\n",command_buffer);ParseCommandLine(command_buffer,strlen(command_buffer)); //3.分析命令//debug();ExecuteCommand();   //4.执行命令}return 0;
}

三.myshell修改

1.自己执行内建命令

我们的cd ..要父进程自己执行

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;const int basesize = 1024;
const int argvnum = 64;
char* gargv[argvnum];
int gargc = 0;string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}string GetPwd()
{string pwd = getenv("PWD");return pwd.empty() ? "None" : pwd;
}string MakeCommandLine()
{// [ltw@alicloud myshell]$char command_line[basesize];snprintf(command_line,basesize,"[%s@ %s]# ",\GetUserName().c_str(),GetPwd().c_str());return command_line;
}void PrintCommandLine()
{printf("%s",MakeCommandLine().c_str());fflush(stdout);
}bool GetCommandLine(char command_buffer[],int size)
{//我们认为:我们要将用户输入的命令行,当做一个完整的字符串//"ls -a -l -n"char* result = fgets(command_buffer,size,stdin);if(!result){return false;}command_buffer[strlen(command_buffer) - 1] = 0;if(strlen(command_buffer) == 0){return false;}return true;
}void ParseCommandLine(char command_buffer[],int len) //3.分析命令
{memset(gargv,0,sizeof(gargv));gargc = 0;// "ls -a -l -n"const char *sep = " ";gargv[gargc++] = strtok(command_buffer,sep);//这里直接就能写完while(gargv[gargc++] = strtok(nullptr,sep));gargc--;
}void debug()
{printf("argc: %d\n",gargc);for(int i = 0;gargv[i];i++){printf("argv[%d]: %s\n",i,gargv[i]);}
}bool ExecuteCommand()   //4.执行命令
{//子进程执行命令pid_t id = fork();if(id < 0){return false;}else if(id == 0){//子进程//1.执行命令execvp(gargv[0],gargv);//2.退出exit(1);}int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){// Do Nothingreturn true;}return false;
}// shell自己执行命令,本质上是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{if(strcmp(gargv[0],"cd") == 0){if(gargc == 2){chdir(gargv[1]);}return true;}return false;
}int main()
{char command_buffer[basesize];while(true){PrintCommandLine(); //1.命令行提示符if(!GetCommandLine(command_buffer,basesize))   //2.获取用户输入{continue;}//printf("%s\n",command_buffer);ParseCommandLine(command_buffer,strlen(command_buffer)); //3.分析命令//debug();if(CheckAndExecBuiltCommand()){continue;}ExecuteCommand();   //4.执行命令}return 0;
}

2.从系统中获取我们的PWD

因为我们的路径发生改变,但是环境变量没有变,所以我们要更新环境变量(环境变量是要进行维护的)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;const int basesize = 1024;
const int argvnum = 64;//全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;//全局的当前shell工作路径
char pwd[basesize];string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}string GetPwd()
{if(nullptr == getcwd(pwd,sizeof(pwd))){return "None";}return pwd;
}string MakeCommandLine()
{// [ltw@alicloud myshell]$char command_line[basesize];snprintf(command_line,basesize,"[%s@ %s]# ",\GetUserName().c_str(),GetPwd().c_str());return command_line;
}void PrintCommandLine()
{printf("%s",MakeCommandLine().c_str());fflush(stdout);
}bool GetCommandLine(char command_buffer[],int size)
{//我们认为:我们要将用户输入的命令行,当做一个完整的字符串//"ls -a -l -n"char* result = fgets(command_buffer,size,stdin);if(!result){return false;}command_buffer[strlen(command_buffer) - 1] = 0;if(strlen(command_buffer) == 0){return false;}return true;
}void ParseCommandLine(char command_buffer[],int len) //3.分析命令
{memset(gargv,0,sizeof(gargv));gargc = 0;// "ls -a -l -n"const char *sep = " ";gargv[gargc++] = strtok(command_buffer,sep);//这里直接就能写完while(gargv[gargc++] = strtok(nullptr,sep));gargc--;
}void debug()
{printf("argc: %d\n",gargc);for(int i = 0;gargv[i];i++){printf("argv[%d]: %s\n",i,gargv[i]);}
}bool ExecuteCommand()   //4.执行命令
{//子进程执行命令pid_t id = fork();if(id < 0){return false;}else if(id == 0){//子进程//1.执行命令execvp(gargv[0],gargv);//2.退出exit(1);}int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){// Do Nothingreturn true;}return false;
}// shell自己执行命令,本质上是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{if(strcmp(gargv[0],"cd") == 0){if(gargc == 2){chdir(gargv[1]);}return true;}return false;
}int main()
{char command_buffer[basesize];while(true){PrintCommandLine(); //1.命令行提示符if(!GetCommandLine(command_buffer,basesize))   //2.获取用户输入{continue;}//printf("%s\n",command_buffer);ParseCommandLine(command_buffer,strlen(command_buffer)); //3.分析命令//debug();if(CheckAndExecBuiltCommand()){continue;}ExecuteCommand();   //4.执行命令}return 0;
}

3.更新我们的环境变量

但是我们的环境变量还是没有进行修改

string GetPwd()
{if(nullptr == getcwd(pwd,sizeof(pwd))){return "None";}snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);putenv(pwdenv);return pwd;
}

4.增加自己的环境变量表

如果我们自己维护了一张表,那么我们要是直接 export(导入环境变量) 或者 echo PATH等命令,就都不需要我们自己创建子进程来维护了

//今天我们的shell就直接从父shell中获取环境变量
void InitEnv()
{extern char** environ;int index = 0;while(environ[index]){genv[index] = (char*)malloce(strlen(environ[index] + 1));strncpy(genv[index],environ[index],strlen(environ[index])]);index++;}genv[index] = nullptr;
}

5.增加内建命令

a.cd 

已经写了,所以就不讲了

b.export

我们添加了自己的环境变量表,所以export导入环境变量的时候,要我们自己(主进程)添加到自己的环境变量中

// shell自己执行命令,本质上是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{if(strcmp(gargv[0],"cd") == 0){if(gargc == 2){chdir(gargv[1]);}return true;}else if(strcmp(gargv[0],"export") == 0){if(gargc == 2){AddEnv(gargv[1]);}return true;}return false;
}

c.env

// shell自己执行命令,本质上是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{if(strcmp(gargv[0],"cd") == 0){if(gargc == 2){chdir(gargv[1]);}return true;}else if(strcmp(gargv[0],"export") == 0){if(gargc == 2){AddEnv(gargv[1]);}return true;}else if(strcmp(gargv[0],"env") == 0){for(int i = 0;genv[i]; i++){printf("%s\n",genv[i]);}return true;}return false;
}

d.将环境变量传递给子进程

bool ExecuteCommand()   //4.执行命令
{//子进程执行命令pid_t id = fork();if(id < 0){return false;}else if(id == 0){//子进程//1.执行命令execvpe(gargv[0],gargv,genv);//2.退出exit(1);}int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){// Do Nothingreturn true;}return false;
}

6.测试环境变量导入是否成功

我们写了一个测试文件,用于检测环境变量

/*************************************************************************> File Name: testenv.c> Author: ma6174> Mail: ma6174@163.com > Created Time: Fri 31 Oct 2025 02:08:08 PM CST************************************************************************/#include<stdio.h>int main(int argc,char* argv[],char* env[])
{for(int i = 0;i < argc;i++){printf("argv[%d]: %s\n",i,argv[i]);}for(int i = 0; env[i]; i++){printf("env[%d]: %s\n",i,env[i]);}return 0;
}

7.获取内建命令和子进程的退出信息

补充知识: 快速定位函数 ctrl + # 

a.内建命令

// shell自己执行命令,本质上是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{if(strcmp(gargv[0],"cd") == 0){if(gargc == 2){chdir(gargv[1]);lastcode = 0;}else{lastcode = 1;}return true;}else if(strcmp(gargv[0],"export") == 0){if(gargc == 2){AddEnv(gargv[1]);lastcode = 0;}else{lastcode = 2;}return true;}else if(strcmp(gargv[0],"env") == 0){for(int i = 0;genv[i]; i++){printf("%s\n",genv[i]);}lastcode = 0;return true;}else if(strcmp(gargv[0],"echo") == 0){if(gargc == 2){//echo $?//echo helloif(gargv[1][0] == '$'){if(gargv[1][1] == '?'){printf("%d\n",lastcode);lastcode = 0;}}else{printf("%s\n",gargv[1]);lastcode = 0;}}else{lastcode = 3;}return true;}return false;
}

我们要将lastcode存储进程退出的信息

b.子进程退出信息获取

bool ExecuteCommand()   //4.执行命令
{//子进程执行命令pid_t id = fork();if(id < 0){return false;}else if(id == 0){//子进程//1.执行命令execvpe(gargv[0],gargv,genv);//2.退出exit(1);}int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){if(WIFEXITED(status)){lastcode = WEXITSTATUS(status);}else{lastcode = 100;}return true;}return false;
}

8.获取最后一个目录

string LastDir()
{string curr = GetPwd();if(curr == "/" || curr == "None"){return curr;}string lastdir = curr.substr(curr.rfind("/") + 1);return lastdir;
}

四.myshell总代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;//全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;//全局的变量
int lastcode = 0;//我的系统的环境变量
char* genv[envnum];//全局的当前shell工作路径
char pwd[basesize];
char pwdenv[basesize + 4];string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}string GetPwd()
{if(nullptr == getcwd(pwd,sizeof(pwd))){return "None";}snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);putenv(pwdenv);return pwd;
}string LastDir()
{string curr = GetPwd();if(curr == "/" || curr == "None"){return curr;}string lastdir = curr.substr(curr.rfind("/") + 1);return lastdir;
}string MakeCommandLine()
{// [ltw@alicloud myshell]$char command_line[basesize];snprintf(command_line,basesize,"[%s@ %s]# ",\GetUserName().c_str(),LastDir().c_str());return command_line;
}void PrintCommandLine()
{printf("%s",MakeCommandLine().c_str());fflush(stdout);
}bool GetCommandLine(char command_buffer[],int size)
{//我们认为:我们要将用户输入的命令行,当做一个完整的字符串//"ls -a -l -n"char* result = fgets(command_buffer,size,stdin);if(!result){return false;}command_buffer[strlen(command_buffer) - 1] = 0;if(strlen(command_buffer) == 0){return false;}return true;
}void ParseCommandLine(char command_buffer[],int len) //3.分析命令
{memset(gargv,0,sizeof(gargv));gargc = 0;// "ls -a -l -n"const char *sep = " ";gargv[gargc++] = strtok(command_buffer,sep);//这里直接就能写完while(gargv[gargc++] = strtok(nullptr,sep));gargc--;
}void debug()
{printf("argc: %d\n",gargc);for(int i = 0;gargv[i];i++){printf("argv[%d]: %s\n",i,gargv[i]);}
}bool ExecuteCommand()   //4.执行命令
{//子进程执行命令pid_t id = fork();if(id < 0){return false;}else if(id == 0){//子进程//1.执行命令execvpe(gargv[0],gargv,genv);//2.退出exit(1);}int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){if(WIFEXITED(status)){lastcode = WEXITSTATUS(status);}else{lastcode = 100;}return true;}return false;
}void AddEnv(const char* item)
{int index = 0;while(genv[index]){index++;}genv[index] = (char*)malloc(strlen(item) + 1);strncpy(genv[index],item,strlen(item) + 1);genv[++index] = nullptr;
}// shell自己执行命令,本质上是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{if(strcmp(gargv[0],"cd") == 0){if(gargc == 2){chdir(gargv[1]);lastcode = 0;}else{lastcode = 1;}return true;}else if(strcmp(gargv[0],"export") == 0){if(gargc == 2){AddEnv(gargv[1]);lastcode = 0;}else{lastcode = 2;}return true;}else if(strcmp(gargv[0],"env") == 0){for(int i = 0;genv[i]; i++){printf("%s\n",genv[i]);}lastcode = 0;return true;}else if(strcmp(gargv[0],"echo") == 0){if(gargc == 2){//echo $?//echo helloif(gargv[1][0] == '$'){if(gargv[1][1] == '?'){printf("%d\n",lastcode);lastcode = 0;}}else{printf("%s\n",gargv[1]);lastcode = 0;}}else{lastcode = 3;}return true;}return false;
}//今天我们的shell就直接从父shell中获取环境变量
void InitEnv()
{extern char** environ;int index = 0;while(environ[index]){genv[index] = (char*)malloc(strlen(environ[index] + 1));strncpy(genv[index],environ[index],strlen(environ[index]) + 1);index++;}genv[index] = nullptr;
}int main()
{InitEnv();char command_buffer[basesize];while(true){PrintCommandLine(); //1.命令行提示符if(!GetCommandLine(command_buffer,basesize))   //2.获取用户输入{continue;}//printf("%s\n",command_buffer);ParseCommandLine(command_buffer,strlen(command_buffer)); //3.分析命令//debug();if(CheckAndExecBuiltCommand()){continue;}ExecuteCommand();   //4.执行命令}return 0;
}
http://www.dtcms.com/a/553543.html

相关文章:

  • MSA 基因序列对比差异化 相关使用
  • Kafka(文件)数据存储、清理机制、高性能设计
  • 湖南免费网站建设怀化网站建设有哪些
  • Redis 从基础到实战
  • 投标建设用地是哪个网站广州seo培训
  • 做电影网站需要注意什么软件网站开发 erp系统开发
  • 删除wps的空白页
  • 计算机网络 —— F / 应用层
  • 62 VueComponent watcher 的实现
  • Ethernaut Level 15: Naught Coin - ERC20 approve/transferFrom漏洞
  • PySide6 实现win10 手动与自动切换主题 借助系统托盘
  • 上传项目至Github与从Github克隆项目
  • 做个人网站的步骤上海建筑设计公司平台
  • 如何使用一个模型完成多种交通任务?请看此文
  • 第N2周:构建词典
  • 德意志飞机D328eco携手ADS:CMS+IETM双引擎,点燃通用航空效率革命
  • c#获取当前程序所在目录避坑
  • day59-Shell编程(第五部分)
  • 网站建设客户需求分析调查表江苏国泰做的网站案例
  • VUE3+element plus el-table上下拖拽
  • 【模拟面试|豆包模拟面试-1 Java基础】
  • AI优化亚马逊广告:DeepBI智能化托管让广告运营从负担变轻松,ACOS优化至23%以下
  • 关键词解释:梯度消失(Vanishing Gradient)与 梯度爆炸(Exploding Gradient)
  • 天津网站建设如何最近火爆的新闻
  • Maven内核探秘:从启动到构建全流程
  • CNCC 2025|开源AI基础设施论坛成功举办
  • 开源可信MCP,AICC机密计算新升级!
  • 混元图像3.0开源原生多模态生图新篇章
  • 环境搭建与第一个程序:Hello, Rust!
  • [论文阅读] AI | 大语言模型服务系统服务级目标和系统级指标优化研究