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

网站开发语言字典视频专用客户端app

网站开发语言字典,视频专用客户端app,单页面网站多少钱,网站平台建设费计入什么科目【Linux学习笔记】进程替换和自定义shell 🔥个人主页:大白的编程日记 🔥专栏:Linux学习笔记 文章目录 【Linux学习笔记】进程替换和自定义shell前言一.进程程序替换1.1 替换原理1.2 替换函数1.2.1函数解释1.2.2命名理解 二.自主…

【Linux学习笔记】进程替换和自定义shell

🔥个人主页大白的编程日记

🔥专栏Linux学习笔记


文章目录

  • 【Linux学习笔记】进程替换和自定义shell
    • 前言
    • 一.进程程序替换
      • 1.1 替换原理
      • 1.2 替换函数
      • 1.2.1函数解释
      • 1.2.2命名理解
    • 二.自主Shell命令行解释器
      • 2.1 模块框架图
      • 2.2 目标
      • 2.3 实现原理
      • 2.4 全局变量
      • 2.5 环境变量函数
      • 2.6 初始化环境变量表函数
      • 2.7 输出命令行提示符模块
      • 2.8 提取命令输入模块
      • 2.9 填充命令行参数表模块
      • 2.10 检测并处理内建命令模块
      • 2.11 执行命令模块
      • 2.12 源码
    • 三 总结
    • 后言

前言

哈喽,各位小伙伴大家好!上期我们讲了进程地址空间 今天我们讲的是进程替换和自定义shell。话不多说,我们进入正题!向大厂冲锋!
在这里插入图片描述

一.进程程序替换

fork()之后,父子各自执行父进程代码的一部分如果子进程就想执行一个全新的程序呢?进程的程序替换来完成这个功能!程序替换是通过特定的接口,加载磁盘上的一个全新的程序(代码和数据),加载到调用进程的地址空间中!

1.1 替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。


1.2 替换函数

其实有六种以exec开头的函数,统称exec函数:

 #include <unistd.h>int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg, ...,char *const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execve(const char *path, char *const argv[], char *const envp[]);

1.2.1函数解释

  1. 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  2. 如果调用出错则返回-1
  3. 所以exec函数只有出错的返回值而没有成功的返回值。
    在这里插入图片描述

1.2.2命名理解

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

  • I(list):表示参数采用列表
  • v(vector):参数用数组
  • p(path):有p自动搜索环境变量PATH
  • e(env):表示自己维护环境变量
函数名参数格式是否带路径是否使用当前环境变量
execl列表不是
execlp列表
execle列表不是不是,须自己组装环境变量
execv数组不是
execvp数组
execve数组不是不是,须自己组装环境变量

exec调用举例如下:

#include <unistd.h>int main()
{char *const argv[] = {"ps", "-ef", NULL};char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};execl("/bin/ps", "ps", "-ef", NULL);// 带p的,可以使用环境变量PATH,无需写全路径execlp("ps", "ps", "-ef", NULL);// 带e的,需要自己组装环境变量execle("ps", "ps", "-ef", NULL, envp);execv("/bin/ps", argv);// 带p的,可以使用环境变量PATH,无需写全路径execvp("ps", argv);// 带e的,需要自己组装环境变量execve("/bin/ps", argv, envp);exit(0);
}

事实上,只有execve是真正的系统调用,其它五个函数最终都调用execve,所以execve在man手册第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。下图exec函数簇一个完整的例子:


在这里插入图片描述

二.自主Shell命令行解释器

2.1 模块框架图

2.2 目标

  • 要能处理普通命令
  • 要能处理内建命令
  • 要能帮助我们理解内建命令/本地变量/环境变量这些概念
  • 要能帮助我们理解shell的允许原理

2.3 实现原理

考虑下面这个与shell典型的互动:

[root@localhost epoll]# lsclient.cpp  readme.md  server.cpp  utility.h[root@localhost epoll]# psPID TTY          
TIME CMD3451 pts/0    
3514 pts/0    
00:00:00 bash00:00:00 ps

然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序并等待这个进程结束。 所以要写一个shell,需要循环以下过程:

  1. 获取命令行
  2. 解析命令行
  3. 建立一个子进程(fork)
  4. 替换子进程(execvp)
  5. 父进程等待子进程退出(wait) 根据这些思路,和我们前面的学的技术,就可以自己来实现一个shell了。

2.4 全局变量

  • 我们的shell内部有两张表命令行参数表和环境变量表
  • 同时我们还要定义一张哈希表方便处理别名
  • 定义两个数组用来方便处理记录路径
  • lastcode记录上一次的进程退出码、
  • 宏定义大小方便开辟数组 以及命令行输出格式
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "// 下面是shell定义的全局数据// 1. 命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc = 0;// 2. 环境变量表
#define MAX_ENVS 100
char* g_env[MAX_ENVS];
int g_envs = 0;// 3. 别名映射表
std::unordered_map<std::string, std::string> alias_list;// for test
char cwd[1024];
char cwdenv[1024];// last exit code
int lastcode = 0;

2.5 环境变量函数

环境标量函数直接调用getenv获取在返回即可
注意GetPWD需要ssnprintf格式化写入即可
DirName直接从后面查找分割符\ 然后返回之后的字符串即可

const char* GetUserName()
{const char* name = getenv("USER");return name == NULL ? "None" : name;
}const char* GetHostName()
{const char* hostname = getenv("HOSTNAME");return hostname == NULL ? "None" : hostname;
}const char* GetPwd()
{//const char *pwd = getenv("PWD");const char* pwd = getcwd(cwd, sizeof(cwd));if (pwd != NULL){snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);putenv(cwdenv);}return pwd == NULL ? "None" : pwd;
}const char* GetHome()
{const char* home = getenv("HOME");return home == NULL ? "" : home;
}
string DirName(const char* pwd)
{
#define SEP "/"string ret = pwd;int index = ret.rfind(SEP);if (ret == SEP){return SEP;}if (index == string::npos){return "BUG?";}string t = ret.substr(index+1);return t;
}

2.6 初始化环境变量表函数

直接把enriron指向的环境变量表拷贝到我们的环境变量表里面 为了区分我们的shell和系统的我们在末尾添加上haha区分
在让environ指向我们的环境变量表即可

void InitEnv()
{extern char** environ;memset(g_env, 0, sizeof(g_env));g_envs = 0;for (int i = 0; environ[i]; i++){g_env[i] = (char*)malloc(strlen(environ[i]) + 1);strcpy(g_env[i], environ[i]);g_envs++;}g_env[g_envs++] = "haha";g_env[g_envs] = NULL;for (int i = 0; g_env[i]; i++){putenv(g_env[i]);cout << g_env[i] << endl;}environ = g_env;
}

2.7 输出命令行提示符模块

先定义一个字符输出存储命令行提示符
然后snprintf格式化写入字符数组中 在输出即可

void PrintCommandPrompt()
{char cmd_prompt[COMMAND_SIZE];MakeCommandLine(cmd_prompt, sizeof(cmd_prompt));std::cout << cmd_prompt;
}
void MakeCommandLine(char cmd_prompt[], int size)
{snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}
const char* GetUserName()
{const char* name = getenv("USER");return name == NULL ? "None" : name;
}const char* GetHostName()
{const char* hostname = getenv("HOSTNAME");return hostname == NULL ? "None" : hostname;
}const char* GetPwd()
{//const char *pwd = getenv("PWD");const char* pwd = getcwd(cwd, sizeof(cwd));if (pwd != NULL){snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);putenv(cwdenv);}return pwd == NULL ? "None" : pwd;
}const char* GetHome()
{const char* home = getenv("HOME");return home == NULL ? "" : home;
}
string DirName(const char* pwd)
{
#define SEP "/"string ret = pwd;int index = ret.rfind(SEP);if (ret == SEP){return SEP;}if (index == string::npos){return "BUG?";}string t = ret.substr(index+1);return t;
}

2.8 提取命令输入模块

这里先fgets获取标准输入到字符数组中
然后构造字符串删除erase\0 然后去map中判断是否为别名
如果是直接把value的值拷贝到数组中即可 然后return ture退出
不是别名则把用户回车的\n字符消除 同时如果用户只回车此时n==0
不做处理返回false 其他返回true;

bool GetCommandLine(char* commandline, int size)
{const char* ret=fgets(commandline, size, stdin);if (ret == NULL){return false;}std::string a = ret;a.erase(a.size() - 1, 1);if (alias_list.count(a)){strcpy(commandline, alias_list[a].c_str());return true;}int n = strlen(commandline);commandline[n - 1] = 0;if (n == 0){return false;}return true;
}

2.9 填充命令行参数表模块

先strtok获取指向第一个空格字符串 while的那个ret不为空时填充g_argv参数表 继续分割填充 直到找不到空格 说明字符串分割完毕
最后填充在g_argv表最后填充NULL即可
根据g_argv大小判断是否填充成功 成功返回ture 反之返回flase

bool CommandParse(char* commandline)
{
#define SEP " "g_argc = 0;char* ret = strtok(commandline, SEP);if (ret == NULL){return false;}while (ret){g_argv[g_argc++] = ret;ret = strtok(nullptr, SEP);}g_argv[g_argc] = ret;for (int i = 0; g_argv[i]; i++){cout << g_argv[i] << " ";}cout << endl;return g_argc > 0 ? true : false;
}

2.10 检测并处理内建命令模块

先根据g_argv表的第一个命令 判断分流检测处理

  • cd命令如果只有cd 那就直接获取家目录的环境变量字符串

  • chdir修改当前命令为家目录即可

  • 否则直接chdir修改当前目录的路径为g_argv[1]

  • echo命令判断分流

  • echo $?直接返回lastcode退出码 再设置wield0即可

  • echo $xxx 直接获取xxx的环境变量 再输出即可

  • echo xxx 直接打印xxx字符串g_argv[1]即可

  • export命令先判断是否填写了要导入的环境变量

  • 没有直接返回ture不做处理 否则直接putenv导入g_argv[1]环境变量即可
    alias命令 这里只处理不带命令选项的替换
    strtok分割=前后字符串然后 存储到map中即可

bool CheckAndExecBuiltion()
{std::string t = g_argv[0];if (t == "cd"){return Cd();}else if (t == "echo"){return Echo();}else if (t == "export"){return Export();}else if (t == "alias"){cout << "开始替换" << endl;return Alias();}else{return false;}
}
bool Cd()
{std::string t;if (g_argc == 1){t = GetHome();if (t == ""){return true;}chdir(t.c_str());}else{t =g_argv[1];chdir(t.c_str());}return true;
}
bool Echo()
{std::string t = g_argv[1];if (t=="$?"){std::cout << lastcode << std::endl;lastcode = 0;}else if (t[0] == '$'){char* ret = getenv(t.substr(1).c_str());if (ret){cout << ret << endl;}}else{cout << t << endl;}return true;
}
bool Export()
{if (g_argc != 2){return true;}putenv(g_argv[1]);return true;
}
bool Alias()
{
#define SEP "="char* t = g_argv[1];char* ret = strtok(t, SEP);if (ret == NULL){return false;}string a=std::string(ret),b = std::string(strtok(NULL, SEP));alias_list[a] = b;cout << alias_list[a] <<" "<<alias_list.count(a)<<endl;cout << a<< "->" << b << endl;return true;
}

2.11 执行命令模块

直接创建子进程 紫金陈通过execvp程序替换执行命令 执行完后exit退出 然后父进程waitpid等待子进程 同时把lastcode更新即可。

int Execute()
{pid_t id = fork();if (id == 0){execvp(g_argv[0], g_argv);exit(1);}int status = 0;pid_t rid=waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);}return 0;
}
  • 效果演示:



在这里插入图片描述

2.12 源码

#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string> 
#include<cstring>
#include<unordered_map>
using namespace std;
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "// 下面是shell定义的全局数据// 1. 命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc = 0;// 2. 环境变量表
#define MAX_ENVS 100
char* g_env[MAX_ENVS];
int g_envs = 0;// 3. 别名映射表
std::unordered_map<std::string, std::string> alias_list;// for test
char cwd[1024];
char cwdenv[1024];// last exit code
int lastcode = 0;
const char* GetUserName()
{const char* name = getenv("USER");return name == NULL ? "None" : name;
}const char* GetHostName()
{const char* hostname = getenv("HOSTNAME");return hostname == NULL ? "None" : hostname;
}const char* GetPwd()
{//const char *pwd = getenv("PWD");const char* pwd = getcwd(cwd, sizeof(cwd));if (pwd != NULL){snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);putenv(cwdenv);}return pwd == NULL ? "None" : pwd;
}const char* GetHome()
{const char* home = getenv("HOME");return home == NULL ? "" : home;
}
void InitEnv()
{extern char** environ;memset(g_env, 0, sizeof(g_env));g_envs = 0;for (int i = 0; environ[i]; i++){g_env[i] = (char*)malloc(strlen(environ[i]) + 1);strcpy(g_env[i], environ[i]);g_envs++;}g_env[g_envs++] = "haha";g_env[g_envs] = NULL;for (int i = 0; g_env[i]; i++){putenv(g_env[i]);cout << g_env[i] << endl;}environ = g_env;
}
bool GetCommandLine(char* commandline, int size)
{const char* ret=fgets(commandline, size, stdin);if (ret == NULL){return false;}std::string a = ret;a.erase(a.size() - 1, 1);if (alias_list.count(a)){strcpy(commandline, alias_list[a].c_str());return true;}int n = strlen(commandline);commandline[n - 1] = 0;if (n == 0){return false;}return true;
}
string DirName(const char* pwd)
{
#define SEP "/"string ret = pwd;int index = ret.rfind(SEP);if (ret == SEP){return SEP;}if (index == string::npos){return "BUG?";}string t = ret.substr(index+1);return t;
}
void MakeCommandLine(char cmd_prompt[], int size)
{snprintf(cmd_prompt, size, FORMAT, GetUserName(), "@hcss-ecs-8ddb:", DirName(GetPwd()).c_str());
}
void PrintCommandPrompt()
{char cmd_prompt[COMMAND_SIZE];MakeCommandLine(cmd_prompt, sizeof(cmd_prompt));std::cout << cmd_prompt;
}
bool CommandParse(char* commandline)
{
#define SEP " "g_argc = 0;char* ret = strtok(commandline, SEP);if (ret == NULL){return false;}while (ret){g_argv[g_argc++] = ret;ret = strtok(nullptr, SEP);}g_argv[g_argc] = ret;for (int i = 0; g_argv[i]; i++){cout << g_argv[i] << "#";}cout << endl;return g_argc > 0 ? true : false;
}
bool Cd()
{std::string t;if (g_argc == 1){t = GetHome();if (t == ""){return true;}chdir(t.c_str());}else{t =g_argv[1];chdir(t.c_str());}return true;
}
bool Echo()
{if (g_argc != 2){return true;}std::string t = g_argv[1];if (t=="$?"){std::cout << lastcode << std::endl;lastcode = 0;}else if (t[0] == '$'){char* ret = getenv(t.substr(1).c_str());if (ret){cout << ret << endl;}}else{cout << t << endl;}return true;
}
bool Export()
{if (g_argc != 2){return false;}putenv(g_argv[1]);
}
bool Alias()
{
#define SEP "="char* t = g_argv[1];char* ret = strtok(t, SEP);if (ret == NULL){return false;}string a=std::string(ret),b = std::string(strtok(NULL, SEP));alias_list[a] = b;cout << alias_list[a] <<" "<<alias_list.count(a)<<endl;cout << a<< "->" << b << endl;return true;
}
bool CheckAndExecBuiltion()
{std::string t = g_argv[0];if (t == "cd"){return Cd();}else if (t == "echo"){return Echo();}else if (t == "export"){return Export();}else if (t == "alias"){cout << "开始替换" << endl;return Alias();}else{return false;}
}
int Execute()
{pid_t id = fork();if (id == 0){execvp(g_argv[0], g_argv);exit(1);}int status = 0;pid_t rid=waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);}return 0;
}
int main()
{//初始化环境变量表InitEnv();while (1){//打印命令行提示符PrintCommandPrompt();//获取命令行输入char commandline[COMMAND_SIZE];if (!GetCommandLine(commandline, sizeof(commandline))){continue;}//填充命令行参数表if (!CommandParse(commandline)){continue;}//处理内建命令if (CheckAndExecBuiltion()){continue;}//执行命令Execute();}return  0;
}

三 总结

在继续学习新知识前,我们来思考函数和进程之间的相似性 exec/exit就像call/return
一个C程序有很多函数组成。一个函数可以调用另外一个函数,同时传递给它一些参数。被调用的函数执行一定的操作,然后返回一个值。每个函数都有他的局部变量,不同的函数通过cal/return系统进行通信。

后言

这就是=进程替换和自定义shell。大家自己好好消化!今天就分享到这! 感谢各位的耐心垂阅!咱们下期见!拜拜~


文章转载自:

http://xMAyu983.sjbpg.cn
http://zTlspFJk.sjbpg.cn
http://O0KLlOa4.sjbpg.cn
http://xCGFHGxp.sjbpg.cn
http://cdy1sgAH.sjbpg.cn
http://QtV4Uw73.sjbpg.cn
http://4bxMxvpR.sjbpg.cn
http://uo5BpOzq.sjbpg.cn
http://lOQo3s2f.sjbpg.cn
http://Ds42Ak8q.sjbpg.cn
http://XNUAW5wB.sjbpg.cn
http://QQBabs7A.sjbpg.cn
http://5L7XronI.sjbpg.cn
http://CPmOjsOo.sjbpg.cn
http://abqKgrum.sjbpg.cn
http://P5twYv5M.sjbpg.cn
http://3UILpJlU.sjbpg.cn
http://61g7QNXp.sjbpg.cn
http://nhoSPLMF.sjbpg.cn
http://QQcBUSqp.sjbpg.cn
http://RyDrBtlv.sjbpg.cn
http://h76T3qbU.sjbpg.cn
http://oJGVMPOh.sjbpg.cn
http://7Yn9abOk.sjbpg.cn
http://SrLw2JES.sjbpg.cn
http://PYyHXJgN.sjbpg.cn
http://btl7n6Ey.sjbpg.cn
http://Byw9AIPI.sjbpg.cn
http://Qzvk7tYS.sjbpg.cn
http://PJclZJyT.sjbpg.cn
http://www.dtcms.com/wzjs/669227.html

相关文章:

  • 中小企业网站制作不了室内设计用什么软件比较好
  • 网站做零售深圳响应式网站设计
  • 设计上海展seo是什么意思职业
  • 织梦网站后台怎么登陆网上商城官网入口
  • 哪个网站做外贸假发好创业商机网官网
  • 打开网站文件夹权限设置搜索引擎网络推广方法
  • 宁波网络建站公司有哪些简述网站规划的主要内容
  • 怎么做网盘网站网址打不开是啥原因
  • 无锡企业建站系统企业网站管理系统带授权
  • 怎么做自己的cms导购网站网站免费源码大全无用下载
  • 移动网站建设哪家便宜做网站比特币钱包
  • 西安市城乡建设管理局网站的公示栏6海口网站开发公司电话
  • 烟台怎么做网站怎么制作一个微信小程序
  • 网站与后台企业网站怎样做可以搜索到
  • 校园网站建设的背景企业网站的建立方法
  • vs 网站开发教程网站备案申请
  • 建一个购物网站香水网络营销策划方案
  • 茶叶网站制作模板简约的网页设计欣赏
  • 网站建设捌金手指下拉十四网站建设公司擅自关闭客户网络
  • dedecms 调用网站内部搜索外贸电商
  • 大连网站设计菲尔莱斯推广宣传方式有哪些
  • 自适应网站开发wordpress怎么实现会员登录
  • 怎样刷新网站做杂志的网站有哪些
  • 网站标题应怎设置企业文化包括哪六个
  • 怎么在wordpress建英文网站怎么制作网站首页的代码
  • 买过域名之前就可以做网站了吗?普通电脑可以做网站服务器
  • 现在网站都是拿什么软件做的如何在微信公众号里建设微网站
  • 怎么查网站空间在哪里网站服务器租用选择
  • 成都网站优化推广怎么建设自己的购物网站
  • 服饰类网站开发项目湖南省建设工程施工合同