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

10.进程控制(下)

一、进程程序替换(重点)

在程序替换过程中,并没有创建新的进程,只是把当前进程的代码和数据用新程序的代码和数据进行覆盖式的替换。

1)一旦程序替换成功,就去执行新代码了,后序代码不执行

2)exec系列函数,只有失败返回值-1,没有成功返回值(成功返回值无意义,已经被替换了)

因此也不用判断返回值,只要返回就是程序替换失败。

认识全部接口:

6个C语言库函数:

#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 execvpe(const char *file, char *const argv[], char *const envp[]);

1)int execl(const char *path, const char *arg, ...);

path:路径名+程序名

arg:可变参数列表

总结:我要执行谁,我想怎么执行它。

execl("/usr/bin/ls","ls","-al",NULL);

必须以NULL结尾,标明参数传递完成。

为什么没有影响父进程?

1.进程具有独立性

2.数据可以发生写时拷贝,代码也可以发生写时拷贝

原理:代码也是通过页表进行虚拟地址到物理内存映射的。

只要是进程,就能进行程序替换。

2)int execlp(const char *file, const char *arg, ...);

p->PATH

file:只告诉需要执行的文件名即可,因为execlp会自动在环境变量PATH中查找指定的命令

execlp("ls","ls","-al",NULL);

3)int execv(const char *path, char *const argv[]);

v->vector

argv:提供一个命令行参数表,指针数组。

char *const argv[] = {(char* const)"ls",(char* const)"-a",(char* const)"-l",NULL
};
execv("/usr/bin/ls",argv);    

4)int execvp(const char *file, char *const argv[]);

v->vector, p->PATH

组合,file表示文件名,argv表示命令行参数表。

char *const argv[] = {(char* const)"ls",(char* const)"-a",(char* const)"-l",NULL
};
execvp("ls",argv);

5)int execvpe(const char *file, char *const argv[], char *const envp[]);

e->env,envp表示环境变量表

新增方式给子进程拿到环境列表:

putenv,然后传递environ全局变量给第三个参数char *const envp[]。

char *const argv[] = {(char* const)"./code",(char* const)"-a",(char* const)"-l",NULL
};  
putenv((char*)"MyVal=123456789");    
extern char **environ;
execvpe("./code",argv,environ);  

6)int execle(const char *path, const char *arg, ..., char * const envp[]);

envp:环境变量表

系统调用execve

#include <unistd.h>
int execve(const char *filename, char *const argv[], char *const envp[]);

C语言封装了系统调用,封装时,若没有显式传的,会默认传。

二、自定义shell

易错点1:

putenv函数:

        使用putenv((char*)env.c_str())时,env是局部变量,函数返回后其内存会被释放,导致环境变量PWD指向无效内存。这会导致后续使用getenv("PWD")时出现未定义行为(如崩溃或垃圾值)。

setenv函数:

        开辟内存,重写一份key=value的键值对。

总结:putenv是要求参数一直都存在在内存中,setenv会生成一份拷贝,即使参数释放了,也没有关系。

setenv第一个参数填键值,第二个参数填值,第三个参数表示如果键值相同值是否修改,0表示不修改,非0表示修改

 自定义shell重点:

bash内存在两张表:命令行参数表 和 环境变量表。

登录时创建bash,bash从系统的配置文件中读取环境变量。

模拟时从bash读,必须开辟空间来存储环境变量表,并维护起来,putenv只是用来建立环境变量的键值对关系的,并不能开辟内存,必须维护环境变量表。

bash通过对用户输入的一行字符串做命令行分析来完成命令行参数表的初始化。

执行命令:

若是内建命令,例如cd,echo,bash亲自执行,其中echo $?,bash会存储上一次进程退出码,cd会改变当前工作目录,以及环境变量中的PWD。

若不是内建命令,例如ls,bash通过创建子进程,子进程程序替换,bash等待子进程来完成。

具体代码如下: 

#include <string>
#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>#define MAX_LINE 1024
#define MAX_ARGC 128//命令行参数表
char *g_argv[MAX_ARGC];
int g_argc = 0;#define MAX_ENVS 200
//环境变量表,键值对                                                                                                                                 
char *g_env[MAX_ENVS];
int g_envs = 0;//上一次程序的退出码
int lastexit = 0;const char* get_username()
{const char* username = getenv((const char*)"USER");return username == NULL ? "None" : username;
}const char* get_hostname()
{const char* hostname = getenv((const char*)"HOSTNAME");return hostname == NULL ? "None" : hostname;
}const char* get_pwd()
{char pwd[MAX_LINE];//const char* pwd = getenv((const char*)"PWD");std::string PWD = getcwd(pwd,MAX_LINE);if(PWD.empty()){return "None";}return PWD.c_str();
}//获取家目录
const char* get_home()
{const char* home = getenv((const char*)"HOME");return home == NULL ? "None" : home;
}                                                                                                                                                    //获取最后一个路径的名称
const char* GetLastPWD()
{std::string pwd = get_pwd();//根目录if(pwd.size()==1)return "/";std::string::reverse_iterator rit = pwd.rbegin();std::string ret;while(rit!=pwd.rend() && *rit!='/'){ret.push_back(*rit);++rit;}reverse(ret.begin(),ret.end());return ret.c_str();
}#define MAX_PROMPT 1024
void MakePrompt(char* prompt)
{
#define STYLE "[%s@%s %s]# "snprintf(prompt,MAX_PROMPT,STYLE,get_username(),get_hostname(),GetLastPWD());                                                                    
}void PrintPrompt()
{//1.打印提示符char prompt[MAX_PROMPT];MakePrompt(prompt);printf("%s",prompt);fflush(stdout);
}bool GetInput(char commandline[])
{char* ret = fgets(commandline,MAX_LINE,stdin);if(ret==NULL)return false;commandline[strlen(commandline)-1]='\0';//获取家目录
const char* get_home()
{const char* home = getenv((const char*)"HOME");return home == NULL ? "None" : home;
}                                                                                                                                                    //获取最后一个路径的名称
const char* GetLastPWD()
{std::string pwd = get_pwd();//根目录if(pwd.size()==1)return "/";std::string::reverse_iterator rit = pwd.rbegin();std::string ret;while(rit!=pwd.rend() && *rit!='/'){ret.push_back(*rit);++rit;}reverse(ret.begin(),ret.end());return ret.c_str();return true;
}bool SpliceString(char commandline[])
{ 
#define space " "g_argc = 0;                                                                                                                                      g_argv[g_argc++] = strtok(commandline,space);while((bool)(g_argv[g_argc++]=strtok(NULL,space)));g_argv[g_argc] = NULL;--g_argc;return true;
}void CD()
{std::string env = "PWD";//修改当前路径为家目录,修改环境变量if(strcmp(g_argv[1],"~")==0){const char* home = get_home();chdir(home);setenv(env.c_str(),home,1);return;}
}void ECHO()
{                                                                                                                                                    std::string content = g_argv[1];if(content[0]=='$'){//上一次程序退出码if(content=="$?"){std::cout << lastexit << std::endl;}//打印环境变量的值else{//获得环境变量的键值std::string env_key(content.begin()+1,content.end());const char* env_value = getenv(env_key.c_str());std::cout << env_value << std::endl;}}else{std::cout << g_argv[1] << std::endl;}
}bool CheckBuiltIncommand()
{if(strcmp(g_argv[0],"cd")==0){                                                                                                                                                CD();lastexit = 0;return true;}else if (strcmp(g_argv[0],"echo")==0){ECHO();lastexit = 0;return true;}return false;
}void Excute()
{pid_t pid = fork();
if(pid==0){execvp(g_argv[0],g_argv);}int status;waitpid(pid,&status,0);lastexit = WEXITSTATUS(status);                                                                                                                  
}void InitEnv()
{//从全局的environ读取环境变量,即从父进程bash读取extern char **environ;g_envs=0;//遍历环境变量,添加到自身环境变量表中for(int i = 0;environ[i];i++){g_env[i] = (char*)malloc(strlen(environ[i])+1);memcpy(g_env[i],environ[i],strlen(environ[i])+1);++g_envs;}//添加const char *myenv = "MYVAL=12345678";g_env[g_envs] = (char*)malloc(strlen(myenv)+1);memcpy(g_env[g_envs],myenv,strlen(myenv)+1);++g_envs;g_env[g_envs] = NULL;for(int i = 0;g_env[i];i++){putenv(g_env[i]);}                                                                                                                                                
}int main()
{InitEnv();char commandline[MAX_LINE];memset(commandline,0,MAX_LINE);while(true){//1.打印提示符PrintPrompt();//2.获取输入的字符串if(!GetInput(commandline) || strlen(commandline)==0)continue;//3.以空格为分割,分割字符串,存到命令行参数表中                                                                                              if(!SpliceString(commandline))continue;//4.检查是否是内建命令,若是由父进程执行if(CheckBuiltIncommand())continue;//5.创建子进程,子进程程序替换执行命令Excute();}return 0;
}
http://www.xdnf.cn/news/339949.html

相关文章:

  • PyTorch 入门与核心概念详解:从基础到实战问题解决
  • 卷积神经网络基础(八)
  • (leetcode) 力扣100 7.接雨水(两种非官解,三种官解,对官解进一步解释)
  • MCP vs Function Call:AI交互的USB-C革命
  • Amazon Redshift 使用场景解析与最佳实践
  • 快速上手Pytorch Lighting框架 | 深度学习入门
  • 华为HCIP-AI认证考试版本更新通知
  • 自定义Widget开发:自定义布局实现
  • Redis 重回开源怀抱:开源精神的回归与未来展望
  • 终极终端体验:Warp 使用完全指南
  • 事务(transaction)-中
  • Opencv进阶操作:图像拼接
  • 【金仓数据库征文】金仓数据库:创新驱动,引领数据库行业新未来
  • 电容知识小结
  • LeetCode第284题 - 窥视迭代器
  • 立式筒仓式发酵槽实验台试验装置
  • Lua从字符串动态构建函数
  • LeetCode 热题 100 238. 除自身以外数组的乘积
  • IC ATE集成电路测试学习——PLL测试(一)
  • Redis-商品缓存
  • pycharm无法导入相对路径下其它文件
  • 性能远超SAM系模型,苏黎世大学等开发通用3D血管分割基础模型
  • 【造包工具】【Xcap】精讲Xcap构造分片包(IPv4、ipv6、4G\5G等pcap均可),图解超赞超详细!!!
  • 开发者如何优雅应对HTTPS抓包难题
  • 智能量化策略开发全流程:数据准备,因子计算,因子分析,模型训练,策略构建(附python代码)
  • 硬件选型:工控机的选择要素
  • 00 Ansible简介和安装
  • ubuntu 22.04 换源
  • 【Linux】FreeRTOS与Linux:实时与通用的终极对比
  • LeetCode热题100--54.螺旋矩阵--中等