进程替换篇

news2024/11/17 3:29:33

文章目录

  • 目录

    前言

    1.进程替换概念

    2.进程替换的原理

    3.进程替换的接口

    4.接口功能验证

    ①execl接口演示

    ②execlp接口演示

     ③execle接口演示

    ④execv接口验证 

     5.尝试写一个自己的shell【了解】


前言

你一定见过类似于这样的“黑框框”,这个“黑框框”其实就是我们的终端,我们可以在这个黑框框中输入相应的命令,来执行一定的功能。在博主的【Linux中的环境变量】一文中,解释了其是命令行中的命令,其实都是一个个被编写好的可执行文件,我们使用对应的命令其实就是在执行相应的程序。那么这个操作到底是怎么实现的呢,我们可不可写一个类似的“黑框框”呢?

图1        计算机中的“框框”

1.进程替换概念

请注意进程的切换与替换是两个完全不同的概念,在博主的【进程的调度与替换】一节中讲解了,进程的切换,如果感兴趣的可以去阅读一下,进程替换顾名思义,就是用另一个进程替换掉当前正在执行的进程。

2.进程替换的原理

还记得我们之前谈论的【进程地址空间】吗?在这篇文章中我们讲解了一个进程所拥有的存储数据的结构mm_struct,我们在这篇文章中介绍到了虚拟地址通过页表转化到物理地址,进程替换的原理就与页表、进程地址空间、物理内存这三者有关,当一个进程使用进程替换接口时,正在按照执行流执行的进程停止向下执行,此时操作系统将需要替换的进程的数据和代码从磁盘中加载到物理内存,并将页表重新映射。进程替换接口调用结束后,原进程的代码和数据都被释放(包括寄存器信息),此时代码从新的代码数据开始执行。

图2        替换进程加载示意图

3.进程替换的接口

#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[]);

说明:

在上述接口中,

①path参数:表示要传递一个文件地址,这一般指的是可执行文件的地址

②file参数:表示要传递一个文件的名字,而后该替换函数会根据环境变量中设置的路径依次递归查找该文件

③arg参数:表示可变参数列表,这个参数必须以NULL结尾(注意不是“NULL”),这个参数等同于你在命令行下想怎么执行这个可执行文件。(有示例代码)

④envp参数:表示向替换程序传递环境变量

⑤argv参数:表示以数组形式向替换程序传递的命令行参数,需要以NULL为结尾。

以上的六个函数接口,只有在失败的时候才会返回,返回值为-1,如果程序替换成功,在该进程中该接口后的代码都不会执行。

命名理解:

l(list) : 表示参数采用列表

v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH

e(env) : 表示自己维护环境变量

4.接口功能验证

注意:在示例代码中,有关主函数的代码中为了追求代码的简便,没有在主函数中设置对紫禁城的等待,这样的作法在工程中是不好的,请不要模仿,这里只是为了演示接口而写的简易代码

①execl接口演示

在使用进程替换之前我们需要一份要替换的程序,在本文中使用一个已经编写好的C++程序(注意:这个被替换的文件可以是任何语言生成的可执行程序),这个示例代码意思是,如果传递的命令行参数小于等于1,那么就报错,如果超过1那么就将这些参数进行打印。

#include <iostream>

using namespace std;
int main(int argc,char *argv[])
{
    if(argc<=1)
    {
        cout<<"未传递足够多的命令行参数"<<endl;
        return 0;
    }

    for(int i=0;argv[i];i++)
    {
        cout<<argv[i]<<endl;
    }
    return 0;
}
图1        程序替换注意事项
#include <iostream>
#include <unistd.h>
int main()
{
    pid_t id=fork();
    if(id==0)
    {
        execl("./exec","./exec","ls",NULL);
    }

    
    return 0;
}
图2        execl程序执行示例图

②execlp接口演示

在该接口的演示程序中,我们使用程序替换执行ls命令行命令,这个命令默认是被加载到环境变量中的,所以我们使用程序替换执行是有结果的,但是如果你想要让你的程序也可以让execlp直接执行,需要将你的程序路径加载的对应的环境变量中,具体操作可参考【Linux中的环境变量】 

图3        execlp接口演示

 ③execle接口演示

我们先将原来的示例代码稍作更改:

#include <iostream>

using namespace std;
int main(int argc,char *argv[],char *env[])
{
    if(argc<=1)
    {
        cout<<"未传递足够多的命令行参数"<<endl;
        return 0;
    }

    for(int i=0;argv[i];i++)
    {
        cout<<argv[i]<<endl;
    }
    for(int i=0;env[i];i++)
    {
        cout<<env[i]<<endl;
    }
    return 0;
}
图4        主函数运行后的结果

可以看到,程序替换后的进程成功拿到了,传递的环境变量数据。

④execv接口验证 

注意对于其他接口中带v的其实本质都一样就是将原来的一个一个分散的参数变为一个数组直接传递过去,这些数组要以NULL作为结尾。

图5        execv接口验证

 5.尝试写一个自己的shell【了解】

一个简单的shell:

#include <iostream>
#include <string>
#include <vector>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#define MAXSIZE 64

void Iteractive(std::string& line)
{
    line="";
    std::cout<<getenv("USER")<<"@"<<getenv("PWD")<<"#: ";
    std::getline(std::cin,line);
}

void GetCommandLine(char *line,char *commandline[])
{
   commandline[0]=strtok(line," ");
   for(int i=1;commandline[i]=strtok(NULL," ");i++);

}

int Execute(char * commandline[])
{
    pid_t id=fork();
    if(id==0)
    {
        execvp(commandline[0],commandline);
    }
    int status;
    pid_t rid =waitpid(id,&status,0);
    return status;
}



int main()
{
    char * commandline[MAXSIZE]={0};
    std::string line;
    //打印交互窗口
    while(1)
    {
        Iteractive(line);

        //获取命令函参数并分割
        GetCommandLine((char *)line.c_str(),commandline);
        //处理内建命令

        //执行命令
        int ret=Execute(commandline);
        if(ret!=0)
        {
            std::cout<<"进程替换出错!"<<std::endl;
            //处理错误
            exit(1);
        }
    }
    return 0;
}

该程序是一个比较简单的版本,只能执行环境变量可自行推测的可执行程序,运行该程序后一些常见的指令都可以执行如:“pwd”、“ls”、“whoami”等,这些可以执行的叫做外部命令,这类命令通常是由独立的可执行文件,当执行这些程序的时候,需要加载到内存。但是有一些指令我们是无法执行的,比如“ll”、“mkdir”、“rm”等,这些不能执行的指令叫做内建命令,这类命令通常是内置在特定的操作系统版本中定制的,不需要加载到内存就可以执行的命令。我们使用exec系列接口只能替换有可执行文件的程序,但是对于内建命令这种不存在可执行程序的命令,我们就需要自己手动来编写来实现该特定的功能。 

这个版本的shell,比上一个代码中,多了内建命令检测的函数,我们将内建命令统一存储在一个全局变量Foreign_key_command中,如果还想添加其它的内建命令除了要自己实现相应的逻辑外,还要在这个全局变量中添加这个命令的名字,本文添加一个内建命令“cd”,其他的命令就不做添加了,这里仅仅是以cd命令作为一个示例。该代码编译后会生成几个警告,无需进行理会,直接执行即可。同时对于该cd命令的逻辑与我们命令行中cd的逻辑有所不同,下示代码是一个“阉割版”的cd命令,没有设置错误检查和对错误地址的检查。

#include <iostream>
#include <string>
#include <vector>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#define MAXSIZE 64
#define FOREIGN_KEY_SIZE 64
char *Foreign_key_command[FOREIGN_KEY_SIZE]={"cd"};


void Iteractive(std::string& line)
{
    line="";
    std::cout<<getenv("USER")<<"@"<<getenv("PWD")<<"#: ";
    std::getline(std::cin,line);
}



void GetCommandLine(char *line,char *commandline[])
{
   commandline[0]=strtok(line," ");
   for(int i=1;commandline[i]=strtok(NULL," ");i++);

}



int Execute(char * commandline[])
{
    pid_t id=fork();
    if(id==0)
    {
        execvp(commandline[0],commandline);
    }
    int status;
    pid_t rid =waitpid(id,&status,0);
    return status;
}


bool Is_Foreign_key(char *command)
{
    for(int i=0;i<FOREIGN_KEY_SIZE;i++)
    {
        if(strcmp(command,Foreign_key_command[i])==0)
        {
            return true;
        }
    }
    return false;
}


void Bulid_externel_command( char * commandline[])
{
    //这里就以cd命令作为示例,不实现其他内建命令。
    if(strcmp(commandline[0],"cd")==0)
    {
        char *address=commandline[1];
        if(address==nullptr)
        {
            address=getenv("HOME");
        }
        
        chdir(address);
        char current_address[1024];
        getcwd(current_address,1024);

        char pwd[1024];
        snprintf(pwd,1024,"PWD=%s",current_address);
        putenv(pwd);
    }
    
}

int main()
{
    char * commandline[MAXSIZE]={0};
    std::string line;
    
    
    //打印交互窗口
    while(1)
    {
        Iteractive(line);

        //获取命令函参数并分割
        GetCommandLine((char *)line.c_str(),commandline);
        //处理内建命令
        if(Is_Foreign_key(commandline[0]))
        {
            Bulid_externel_command(commandline);
        }
        //执行命令
        else
        {
            int ret=Execute(commandline);
            if(ret!=0)
            {
                std::cout<<"进程替换出错!"<<std::endl;
                
                //处理错误/释放资源....

                exit(1);
            }
        }
            
        
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2119929.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

js 请求api + 解析数据 2个例子

起因&#xff0c; 目的: 补补 js 基础。 例1&#xff0c; 请求天气 api&#xff0c; 天气数据api js 中的 await await 关键字只能在 async 函数内部使用。函数内部可以使用 await&#xff0c;但是在函数外部直接使用 await 是不允许的。 async function fetchWeatherData…

[mysql]最基本的SELECT...FROM结构

第0种&#xff1a;最基本的查询语句 SELECT 字段名&#xff0c;字段名 FROM 表名 SELECT 1&#xff1b; SELECT 11,3*2&#xff1b; FROM SELECT 11,3*2 FROM DUAL&#xff1b;#dual&#xff1a;伪表 我们可以用它来保持一个平衡 这里我们的值不需要在任何一个表里&#xf…

MyBaits的初理解

一.Mybaits的简介 Mybaits就是对JDBC的简化&#xff0c;就是对持久化的实现。 二.基础 需要导的dependencies <dependencies><!-- mybatis依赖 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId>&l…

第 5 章多视图几何

本章讲解如何处理多个视图&#xff0c;以及如何利用多个视图的几何关系来恢复照相机位置信息和三维结构。通过在不同视点拍摄的图像&#xff0c;我们可以利用特征匹配来计算出三维场景点以及照相机位置。本章会介绍一些基本的方法&#xff0c;展示一个三维重建的完整例子&#…

获取多行文本内容,去掉首尾空格、去掉空字符串,解析为文本数组

核心代码 // 获取多行文本内容&#xff0c;去掉首尾空格、去掉空字符串&#xff0c;解析为文本数组 getMultiLineTexts(textareaValue) {return textareaValue.split("\n").map((v) > v.split("\t").join("").trim()).filter((v, i, ar) &g…

Qt | ubuntu20.04安装Qt6.5.3并创建一个example完整教程(涉及诸多开发细节,商用慎重)

点击上方"蓝字"关注我们 01、下载 >>> 下载Qt在线安装包 这里采用镜像地址进行下载,避免网络过慢。 镜像地址:http://mirrors.ustc.edu.cn/qtproject/archive/online_installers/4.5/ 选择最新版本下载,如截至目前最新版本为qt-unified-linux-x64-4.5.2…

“探索数字孪生技术:细数其在各行业的实际应用场景“

数字孪生城市是指在数字世界中创建一个同物理实体城市外观一致、行动一致、思想一致的 数字虚拟城市&#xff0c;实现对现实世界的监测、诊断、回溯、预测和决策控制&#xff0c;用于实体城市的规划、建设、 治理和优化等全生命周期管理&#xff0c;提高城市运行效率和市民居住…

内网穿透的应用-Deepin系统安装x11vnc实现任意设备无公网IP远程连接Deepin桌面

文章目录 前言1. 安装x11vnc2. 本地远程连接测试3. Deepin安装Cpolar4. 配置公网远程地址5. 公网远程连接Deepin桌面6. 固定连接公网地址7. 固定公网地址连接测试 前言 本文主要介绍在Deepin系统中安装x11vnc工具&#xff0c;并结合Cpolar内网穿透工具实现任意设备无公网IP也可…

计算机毕业设计Pyhive+Spark招聘可视化 职位薪资预测 招聘推荐系统 招聘大数据 招聘爬虫 大数据毕业设计 Hadoop Scrapy

《SparkHive招聘推荐与预测系统》开题报告 一、引言 随着互联网技术的飞速发展&#xff0c;招聘行业积累了大量的数据&#xff0c;包括职位信息、应聘者信息、企业信息等。这些数据中蕴含着丰富的价值&#xff0c;能够帮助企业和求职者更好地匹配&#xff0c;提高招聘效率。然…

浮点数精度问题

为什么会产生精度问题&#xff1f; 我们带着这个问题去探寻浮点数二进制的存储原理 浮点数是怎么存在计算机中的&#xff1f; 浮点数在计算机中的表示通常遵循IEEE 754标准。其基本概念如下&#xff1a; 结构&#xff1a;浮点数由三部分组成&#xff1a; 符号位&#xff08;…

Android主副屏显示-Android13

Android主副屏显示-Android13 1、DisplayDeviceInfo屏幕信息2、LogicalDisplay.java2.1 configureDisplayLocked刷新 DisplayManagerService启动及主屏添加-Android13 1、DisplayDeviceInfo屏幕信息 DisplayManagerService启动及主屏添加-Android13 中监听获取&#xff0c;在L…

Vue3+ElementPlus 封装图片空间组件的门面

什么是图片空间? 图片空间就是专门管理我们上传图片的地方。就好比用户管理一样&#xff0c;我们对要上传的图片进行管理。 这样做的好处有哪些&#xff1f; 我们把可能需要的图片都上传到图片管理中。在其他需要图片的地方&#xff08;如&#xff1a;商品图片等&#xff09;可…

【MATLAB】FIR滤波器的MATLAB实现

FIR滤波器的MATLAB实现 FIR滤波器的设计fir1函数fir2函数 与IIR滤波器相比&#xff0c;FIR滤波器既有其优势也有其局限性。FIR滤波器的主要优点包括&#xff1a; 精确的线性相位响应&#xff1b;永远保持稳定性&#xff1b;设计方法通常是线性的&#xff1b;在硬件实现中具有更…

Open CASCADE学习|按圆离散旋转体

旋转体是一个非常重要的概念&#xff0c;它涉及到三维空间中由二维曲线绕某一轴线旋转形成的立体形状。这种旋转体的形成过程&#xff0c;实际上是一个连续变化的动态过程&#xff0c;但在数学和几何学中&#xff0c;我们往往通过静态的方式来描述和研究它。 旋转体的基本特性…

中电金信智能视觉分析系统,以AI技术助力企业升级

基于行业需求与业务痛点&#xff0c;中电金信推出了智能视觉分析系统。该系统是集视频接入、视频识别与分析、AI算法管理、异常报警等为一体&#xff0c;可提供视频安全监管标准的场景应用方案以及二次开发能力的通用智能视觉分析系统。该系统拥有强大的监控摄像头设备接入能力…

EFI引导模式下配置Windows和Linux双系统共存

这几天在VirtualBox虚机里玩Modular MAX下的LLama3大模型&#xff0c;实在受不了这执行速度&#xff0c;于是下决心把Ubuntu系统安装在硬盘上跟Windows11做双系统共存。之前在传统BIOS引导模式下做过不少次双系统引导&#xff0c;EFI模式下第一次做&#xff0c;加之windows系统…

【JavaSE】--数据类型与变量

文章目录 1. 字面常量2. 数据类型3. 变量3.1 变量概念3.2 整型变量3.2.1 整型变量3.2.2 长整型变量3.2.3 短整型变量3.2.4 字节型变量 3.3 浮点型变量3.3.1 双精度浮点型3.3.2 单精度浮点型 3.4 字符型变量3.5 布尔型变量3.6 类型转换3.6.1 自动类型转换&#xff08;隐式&#…

Openeuler22 部署 RackTables0.22.0

目录 0、前言 一、部署lamp环境&#xff0c;lamp环境测试 1、部署Apache&#xff0c;apache环境测试 2、部署php、mysql&#xff0c;php环境测试 二、放文件 三、配置mysql 四、安装racktables 第一步、点击proceed继续 第二步、点击proceed 第三步、根据提示进行操作…

【Qt】解决设置QPlainTextEdit控件的Tab为4个空格

前言 PyQt5 是一个用于创建跨平台桌面应用程序的 Python 绑定集合&#xff0c;它提供了对 Qt 应用程序框架的访问。用于开发具有图形用户界面&#xff08;GUI&#xff09;的应用程序&#xff0c;以及非GUI程序。PyQt5 使得 Python 开发者可以使用 Qt 的丰富功能来构建应用程序。…

【无线通信发展史⑨】1791年路易吉·伽伐尼-关于动物电的研究与1800年亚历山大·伏打伯爵-电池:伏打电池

前言&#xff1a;用这几个问答形式来解读下我这个系列的来龙去脉。如果大家觉得本篇文章不水的话希望帮忙点赞收藏加关注&#xff0c;你们的鼓舞是我继续更新的动力。 我为什么会写这个系列呢&#xff1f; 首先肯定是因为我本身就是一名从业通信者&#xff0c;想着更加了解自己…