进程控制第二弹(进程程序替换)

文章目录

  • 代码现象
  • 基本原理
  • 多进程版本
    • 实例
    • 基本原理
  • 使用所有的替换方法,并且认识函数的参数含义
    • execl
    • execv
    • execlp、execvp
    • execvpe
  • 总结

在这里插入图片描述

代码现象

#include<stdio.h>    
#include<unistd.h>    int main()    
{    printf("testexec begin! ...\n");    execl("/usr/bin/ls","ls","-l","-a",NULL);                                                                                              printf("testexec end! ...\n");    return 0;    
} 

在这里插入图片描述

程序运行后,调用execl函数后,我们的程序去执行了ls命令,原来的进程中printf("testexec end! ...\n"); 没有执行。

基本原理

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变

我们知道,进程=内核数据结构+代码数据
程序替换的本质是将当前进程的代码和数据进行替换。

在这里插入图片描述

替换的时候,会不会创建新的进程?
答案是没有!!只不过是拿老程序的壳子执行新程序的代码。

站在被替换进程的角度:本质上是这个程序被加载到内存。使用exec系列函数加载,exec系列函数类似一种Linux上的加载函数。

所以为什么上述现象中,原来的进程中printf("testexec end! ...\n"); 没有执行的原因是,调用execl函数后,去执行ls程序了,原来的代码和数据被替换了。

exec系列函数执行完毕后,后续的代码不见了,因为被替换了,因此没有机会去执行了。

不用关心exec系列函数的返回值,只要替换成功,就不会向后面执行;反之,一定是替换失败。

多进程版本

实例

fork创建子进程,让子进程自己去替换

代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{printf("testexec begin! ...\n");pid_t id=fork();    if(id==0)                           {    sleep(2);         //child    execl("/usr/bin/ls","ls","-l","-a",NULL);    exit(1);                                                                                               }            //father    int status=0;    pid_t rid=waitpid(id,&status,0);    if(rid>0)    {                printf("father wait success,child,exit code:%d\n",WEXITSTATUS(status));    }          printf("testexec end! ...\n");                                               return 0;
}

现象:

在这里插入图片描述

基本原理

创建子进程,子进程完成的任务:

  1. 让子进程执行父进程代码的一部分
  2. 让子进程执行一个全新的程序

在这里插入图片描述

首先父进程和子进程的PCB、虚拟内存构建好后,通过页表映射到物理内存中。可执行程序testexecl从磁盘中加载到物理内存中。在代码中,子进程执行一个新的程序execl("/usr/bin/ls","ls","-l","-a",NULL);此时有一个ls程序需要从磁盘中加载到物理内存中。之前说过,进程具有独立性,即便是父子进程。将ls加载到物理内存时,需要在数据层面上做写时拷贝,然后把ls数据加载进去,修改子进程的映射关系,保证子进程和父进程在数据层面上是独立的。但是ls不仅仅只有数据,还有代码,因此代码也需要发生写时拷贝。虽然代码是可读的,但是在操作系统看来都无所谓。所以重新开辟内存,将ls代码加载到物理内存,修改子进程的映射关系。至此,只要程序替换成功,彻底将子进程和父进程分开了。

使用所有的替换方法,并且认识函数的参数含义

在这里插入图片描述

execl

在这里插入图片描述

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

execl中,l:list,列表

path:需要执行的路劲,需要带路劲
后面的参数:在命令行中怎么执行

例如:

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

在这里插入图片描述


execv

在这里插入图片描述

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

v(vector) : 参数用数组

 if(id==0)    {    sleep(2);    char* const argv[]={"ls","-l","-a","--color",NULL};    //child    // execl("/usr/bin/ls","ls","-l","-a",NULL);    execv("/usr/bin/ls",argv);                                                                                                         exit(1);    }    

在这里插入图片描述


execlp、execvp

execlp(const char *file, const char *arg, ...);
execvp(const char *file, char *const argv[]);

p(path) : 有p自动搜索环境变量PATH,用户可以不传要执行的路劲(但是文件名要传),直接告诉要执行谁即可

if(id==0)    {    sleep(2);    char* const argv[]={"ls","-l","-a","--color",NULL};    //child    // execl("/usr/bin/ls","ls","-l","-a",NULL);    // execv("/usr/bin/ls",argv);    execvp("ls",argv);                                                                                                                 exit(1);    }    

在这里插入图片描述


execvpe

上面的程序替换,我们替换的都是系统的命令,那么可不可以替换我们自己写的程序呢?

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

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

在这里插入图片描述

testexec二进制程序去执行mypragma二进制程序

mypragma.cc代码:

在这里插入图片描述

 if(id==0)    {    sleep(2);    execl("./mypragma","mypragma",NULL);                                                                                                 // sleep(2);    // char* const argv[]={"ls","-l","-a","--color",NULL};    //child    // execl("/usr/bin/ls","ls","-l","-a",NULL);    // execv("/usr/bin/ls",argv);    // execvp("ls",argv);    exit(1);    }    

在这里插入图片描述

此时,我们写的C++程序就被调度了

除了C++语言可以被C语言调度,其他语言也可以被调度,例如python、脚本语言等…

我们知道了这一件事情之后,再谈execvpe函数:

testecel.c文件部分代码:

if(id==0)    {    char* const argv[]={(char*)"mypragma",NULL};    char* const envp[]={(char*)"HAHA=111",(char*)"HEHE=222",NULL};    sleep(2);    // execl("./mypragma","mypragma",NULL);    execvpe("./mypragma",argv,envp);                                                                                                     // sleep(2);    // char* const argv[]={"ls","-l","-a","--color",NULL};    //child    // execl("/usr/bin/ls","ls","-l","-a",NULL);    // execv("/usr/bin/ls",argv);    // execvp("ls",argv);    exit(1);    }    

mypragma.cc代码:

 #include<iostream>    using namespace std;    W>int main(int argc,char* argv[],char* env[])    {    int i=0;    for(;argv[i];i++)    {    printf("argv[%d]:%s\n",i,argv[i]);    }    printf("-----------------------------------\n");    for(i=0;env[i];i++)    {    printf("env[%d]:%s\n",i,env[i]);                                                                                                   }    printf("-----------------------------------\n");    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    return 0;    } 

运行结果:

在这里插入图片描述

结论:我们平时自己运行的程序,命令行参数和环境变量是父进程给你的,父进程自己有一个环境变量表,创建子进程时把对应的信息传递给子进程,execvpe直接交给子进程,环境变量就直接给了子进程。

父进程本身就有一批环境变量,从“爷爷进程”来的,即bash

在这里插入图片描述
在这里插入图片描述

这个传参,如果传的是自定义的环境变量,那么就整体替换所有环境变量

传环境变量有三种情况:

  1. 用全新的给子进程
  2. 用老的环境变量给子进程,environ
  3. 老的环境变量稍作修改,传递给子进程

总结

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

数据安全全面防护

what 通过采用各种有效技术和管理措施来保护网络系统的正常运行&#xff0c;从而保证数据的可用性&#xff0c;机密性&#xff0c;完整性。 ---网络安全防护体系建设三同步--规划 建设 运行 数据安全的三大基本特征 可用性 数据在需要时可用且可访问&#xff0c;为实现可用…

学习JavaEE的日子 Day39 注解,反射

Day39 注解 1.什么是注解 理解&#xff1a;给程序员和程序解释代码信息 2.注解与注释的区别 注释&#xff1a;对程序员解释代码信息 注解&#xff1a;对程序和程序员解释代码信息 3.注解的所用 可以作用于包、类、方法、属性上&#xff0c;给他们添加额外的信息&#xff0c;可…

Fastadmin解决异步高并发大并发阻塞超时问题

官方连接​​​​​​https://guzzle-cn.readthedocs.io/zh_CN/latest/quickstart.html?highlightgetasync 使用guzzle 实现&#xff0c;需要先引用。安装请自行解决。 use GuzzleHttp\Client; use GuzzleHttp\Promise; /*** 异步高并发请求*/public function asyncRequests…

Android自定义类-写字板

目录 1. 属性文件 res/values/attrs.xml 2. 自定义控件类文件 MyClipbroad.class 3. XML布局文件中的使用 4. Java文件中的使用 该写字板可设置画笔颜色、画笔宽度、画布背景&#xff0c;具有导出图像、清空画布功能&#xff0c;可与OnTouchListener配合达到触摸绘画的效果…

速盾:cdn都能防御哪些攻击?

CDN&#xff08;Content Delivery Network&#xff09;是一种分布式的服务器网络&#xff0c;通过将资源缓存到多个服务器节点上&#xff0c;在用户请求资源时将其分发至最近的服务器节点&#xff0c;提供快速响应和高可用性。除了提供高速内容传输和优化用户体验之外&#xff…

算法课程笔记——STL键值对map

map当下标无限的数组 重点是对应关系&#xff0c;一般不修改compare 类比set 没有lowerbound&#xff0c;因为遍历是无序的 ; map不能用sort函数排序 但可用vector转化为map使用 std::set<std::pair<TKEY, mutable TVAL> > ≈ std::map<TKEY, TVAL>

使用 Cucumber框架进行BDD测试的一些项目

BehatMage 项目地址: https://github.com/MageTest/BehatMage 不过该项目在GitHub中有超过10年没有更新了。 项目介绍&#xff1a; BehatMage项目介绍 BehatMage是一个基于Behat的Magento测试框架&#xff0c;用于自动化测试Magento电子商务平台的功能和性能。Behat是一个行…

什么是认知负荷?

认知负荷&#xff08;Cognitive Load&#xff09;是心理学中的一个概念&#xff0c;它描述了一个人在特定时间内处理信息和进行思考所需耗费的认知资源的量。这个概念是由教育心理学家John Sweller在1988年提出的&#xff0c;主要用于描述学习过程中的认知需求。 认知负荷通常…

【剪映专业版】13快速为视频配好音:清晰、无噪声、对齐

视频课程&#xff1a;B站有知公开课【剪映电脑版教程】 使用场景&#xff1a;视频无声音或者视频有声音但是需要更改声音 时间指示器在哪里&#xff0c;就从哪里开始 红色按钮&#xff1a;开始录音 声音波纹&#xff1a;蓝色最佳&#xff0c;黄色或红色声音太大&#xff0c;…

23种设计模式之创建型模式篇

一、创建型模式 这类模式主要关注对象的创建过程。它们试图在创建对象的同时&#xff0c;将对象的创建和使用分离&#xff0c;以达到更高的灵活性和可扩展性. 包括: 工厂方法模式&#xff08;Factory Method&#xff09;抽象工厂模式&#xff08;Abstract Factory&#xff0…

C# 反射基础

1 在.NET中程序集是什么&#xff1f; 程序集&#xff08;Assembly&#xff09;以可执行文件(.exe)或动态链接库文件(.dll)的形式存在&#xff0c;是.NET应用程序构建的基本单元。程序集可用于部署、版本控制和设置安全权限等。 2.程序集的有哪些部分组成&#xff1f; 程序集…

Unity UGUI透明区域点击无效

是这样的&#xff0c;我有一张图&#xff0c;客户给的是1920*1080&#xff0c;但只有中间部分是按钮&#xff0c;是有效像素。为了让空白区域点击无效。需要设置如下 并且加上下面这句 this.GetComponent<Image>().alphaHitTestMinimumThreshold 0.1f;

tomcat中Pipeline-Valve解析

tomcat中用到Pipeline-Value有二种方式&#xff0c;一种是管道/阀门&#xff0c;另一种是过滤器链/过滤器。 管道/阀门过滤器链/过滤器管道&#xff08;Pipeline&#xff09;过滤器链&#xff08;FilterChain&#xff09;阀门&#xff08;Valve&#xff09;过滤器&#xff08;…

python学习笔记B-08:序列结构之列表--列表的遍历操作

列表的遍历方法主要有三种&#xff0c;使用方法和特点如下&#xff1a; lst list("hello") print("第一种遍历方式&#xff0c;使用for循环&#xff0c;循环变量item直接就是lst中的元素") for item in lst:print(item,end"\t")print("\n…

LeetCode 383.赎金信(模拟,for(char c : 容器)的使用)

给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 示例 1&#xff1a; 输入&#…

第64天:服务攻防-框架安全CVE复现Apache ShiroApache Solr

目录 思维导图 案例一&#xff1a;Apache Shiro-组件框架安全 shiro反序列化 cve_2016_4437 CVE-2020-17523 CVE-2020-1957 案例二&#xff1a;Apache Solr-组件框架安全 远程命令执行 RCE&#xff08;CVE-2017-12629&#xff09; 任意文件读取 AND 命令执行&#xff08…

【java】(软考)面向对象---责任链解析

目录 责任链的意义 手写笔记 ​编辑 责任链的意义 当您把请求给出时&#xff0c;如果某对象不能实现您的操作&#xff0c;责任链会自动把您的请求传给它的下一级 从而避免请求的发送者和接受者之间的耦合关系 这里以2007年下半年试题七进行说明 题目描述 某企业的采购审批…

SpringBoot学习之Kafka下载安装和启动(三十三)

一、Mac环境 1、下载Kafka&#xff1a;Apache Kafka 2、这里我选择的版本是kafka_2.12-3.7.0&#xff0c;下载最新版的Kafka二进制文件&#xff0c;解压到你喜欢的目录&#xff08;建议目录不要带中文&#xff09;。 3、启动ZooKeeper服务&#xff0c;Kafka需要使用ZooKeeper&…

OpenHarmony 网络管理-Socket连接

介绍 本示例主要演示了Socket在网络通信方面的应用&#xff0c;展示了Socket在两端设备的连接验证、聊天通信方面的应用。 效果预览 使用说明 1.搭建服务器环境&#xff1a;修改服务器脚本中的服务端IP地址&#xff0c;与本机IP地址保持一致&#xff0c;修改完成后双击运行脚…

Zynq 7000 系列中的JTAG和DAP子系统

Zynq 7000系列SoC器件通过标准JTAG调试接口提供调试访问。在内部&#xff0c;SoC设备器件在处理系统&#xff08;PS&#xff09;内部实现了一个Arm调试访问端口&#xff08;DAP&#xff09;&#xff0c;同时在可编程逻辑&#xff08;PL&#xff09;内部实现了一个标准的JTAG测试…