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

Linux:进程控制(二.详细讲解进程程序替换)

上次讲了:Linux:进程地址空间、进程控制(一.进程创建、进程终止、进程等待)


文章目录

  • 1.进程程序替换
    • 1.1概念
    • 1.2原理
    • 1.3使用一个`exec` 系列函数
      • execl()函数
      • 结论与细节
  • 2.多进程时的程序替换
  • 3.其他几个exec系列函数
    • 也可以调用其他语言的程序
      • 想要生成两个可执行文件的makefile


1.进程程序替换

之前我们进行的程序演示里,都只能运行自己的代码。那我们怎么样才能执行其他程序的代码呢?(例如在程序里使用ls之类的指令)就可以使用进程程序替换,一开始我们先只看单进程的情况。后面在引入多进程的情况

1.1概念

进程程序替换是指在运行过程中将一个进程的地址空间中的代码、数据和堆栈等内容完全替换为另一个程序的代码、数据和堆栈的过程。这个过程通常是由操作系统提供的 exec 系列函数来实现的:

  • 地址空间替换:进程的地址空间是指进程可以访问的内存范围。通过地址空间替换,进程可以在运行时动态地加载并执行不同的程序,从而实现灵活的程序执行和管理。

  • exec 函数族exec 函数族是一组系统调用,用于执行程序替换操作。这些函数包括 execl, execv, execle, execve 等,它们允许以不同的方式传递参数给新程序,并执行地址空间替换。

    我们要改变内存,那肯定是要调用系统调用接口的,这些函数会封装相应的接口

  • 程序入口点:新程序的入口点是程序中的起始执行位置,通常是 main 函数或其他指定的入口函数。替换完成后,控制权将转移到程序入口点,开始执行新程序的代码。

1.2原理

  • 当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换
  • 替换完成后,控制权将转移到新程序的入口点,开始执行新程序的代码。

在这里插入图片描述

1.3使用一个exec 系列函数

execl()函数

execl函数是Linux系统中用于执行新程序的函数之一,它属于exec函数族的一部分。这个函数的作用是在当前进程的上下文中启动一个新的程序,并替换当前进程的映像为新的程序映像。调用execl函数后,当前进程将停止执行,并由新的程序开始执行

#include<unistd.h>
int execl(const char *path, const char *arg0, ... /* (char  *) NULL */);

参数说明:

  • path:要执行的程序的路径。
  • arg0:新程序的参数列表的开始,通常这会是新程序的名称(尽管这不是强制的,但它通常用于错误消息和程序内部)。
  • ...:一个可变参数列表(参数的数量不固定),新程序的参数列表,必须以NULL结尾。

execl函数会根据提供的路径path找到并执行相应的程序,同时arg0及其后面的参数作为新程序的命令行参数传递。注意,参数列表必须以NULL结尾,这是告诉execl参数列表结束的标志。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");int a=execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("execl end...\n");return 0;
}

在这里插入图片描述

如果execl函数调用成功,那么它实际上不会返回,因为当前进程的映像已经被新程序替换。如果调用失败,它会返回-1,并设置全局变量errno以指示错误原因。常见的错误原因可能包括文件未找到、权限不足等。

execl函数和其他exec函数一样,不会创建新的进程。它们只是在当前进程的上下文中启动另一个程序

因此,调用execl前后,进程的ID(PID)不会改变。同时,由于execl会替换整个进程映像,所以在调用execl之前,通常需要确保当前进程的所有打开的文件描述符、内存分配等都被适当地处理或释放,因为这些资源不会被新程序继承。

结论与细节

  1. 程序替换一旦成功,exec后面的代码不在执行。因为被替换掉了,这也是什么代码没有输出execl end的原因了

  2. exec函数调用成功,那么它实际上不会有返回值;调用失败,它会返回-1

  3. exec函数不会创建新的进程。它们只是在当前进程的上下文中启动另一个程序

  4. 创建一个进程。我们是先创建PCB、地址空间、页表等再先把程序加载到内存

    先加载的话,页表都没办法映射的

  5. 程序替换的本质就是加载 (可以看成一个加载器),有替换就是替换,没有就是程序加载

    程序替换的本质是程序加载,因为在执行 exec 函数时,操作系统会加载新程序的可执行文件,并将其代码、数据和堆栈等部分加载到进程的地址空间中。这个过程涉及将新程序的内容从磁盘加载到内存中,为进程提供执行所需的资源。因此,虽然我们常说是“程序替换”,但实际上更准确地说是将新程序加载到内存中,替换掉原有的程序,以实现进程的功能切换和更新。

  6. 程序运行要加载到内存;为什么?冯诺依曼体系规定;如何加载的呢?就是程序替换:程序替换是操作系统的接口,所谓的把磁盘里的数据加载到内存就是把磁盘设备的数据拷贝到内存里。把数据从一个硬件搬到另一个硬件,只有操作系统能做

2.多进程时的程序替换

我们可以创建一个子进程,由子进程来进行程序替换,父进程来等待结果就可以。为什么? 父进程能得到子进程的执行结果

我们知道父进程与子进程映射到同一块代码,那么子进程进行程序替换后,不是会覆盖吗,替换为什么不影响父进程?

进程具有独立性,在进行程序替换时要进行写时拷贝

写时拷贝的本质就是开辟新的空间

shell是如何运行起来一个指令的?

首先创建子进程,shell会waitpid()等待进程结果,子进程会继承shell的代码,但是不影响。子进程进行程序替换,替换为我们输入的指令

int main()
{pid_t id = fork();if (id == 0){printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("execl end...\n");exit(1);}pid_t rid = waitpid(id, NULL, 0);if (rid > 0){printf("wait successfully\n");}return 0;
}

在这里插入图片描述

3.其他几个exec系列函数

在这里插入图片描述

  1. execl:该函数允许通过提供可变数量的参数来执行指定的可执行文件。它的原型如下:

    int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
    

    path 是要执行的可执行文件的路径,arg0 是第一个参数,后续参数都是传递给可执行文件的命令行参数,以 NULL 结尾。

  2. execlp:该函数与 execl 类似,但是它会在系统的环境变量 PATH 指定的目录中查找可执行文件。它的原型如下:

    int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
    

    file 是要执行的可执行文件的文件名arg0 是第一个参数,后续参数都是传递给可执行文件的命令行参数,以 NULL 结尾。

    相比于execl函数,execlp函数的第一个参数能直接写文件名,系统会PATH环境变量里去查找

    多的字母p:PATH环境变量

    int main()
    {pid_t id = fork();if (id == 0){printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");execl("ls", "ls", "-a", "-l", NULL);printf("execl end...\n");exit(1);}pid_t rid = waitpid(id, NULL, 0);if (rid > 0){printf("wait successfully\n");}return 0;
    }
    

    在这里插入图片描述

  3. execv:类似于 execl,但是允许传递一个参数数组给被执行的程序。它的原型如下:

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

    path 是要执行的可执行文件的路径argv 是一个NULL 结尾的参数数组,其中每个元素都是一个字符串,表示命令行参数

    相比于exec多个字母v:代表vector

    int main()
    {pid_t id = fork();if (id == 0){printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");char* argv[] = { "ls","-a","-l",NULL};execv("/usr/bin/ls",argv);printf("execl end...\n");exit(1);}pid_t rid = waitpid(id, NULL, 0);if (rid > 0){printf("wait successfully\n");}return 0;
    }
    

    在这里插入图片描述

  4. execvp:类似于 execv,但是它会在系统的环境变量 PATH 指定的目录中查找可执行文件。它的原型如下:

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

file 是要执行的可执行文件的文件名,argv 是一个以 NULL 结尾的参数数组,其中每个元素都是一个字符串,表示命令行参数。

既有字母p 又有v,结合上面那两种就行

  1. execle:函数与 execl 函数类似,但允许在启动新程序时传递额外的环境变量。它的原型如下:

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

    path 是要执行的可执行文件的路径,arg 是要传递给新程序的命令行参数,后面的参数是额外的环境变量,以 NULL 结尾。

进程程序替换不会替换环境变量的

  1. 想要子进程继承全部的环境变量,不用管,直接就能拿到

  2. 单纯新增环境变量,在父进程里使用putenv()函数,会影响子进程

putenv 是 C 语言中的一个库函数,它定义在 <stdlib.h> 头文件中。这个函数用于将字符串添加到环境变量中,或者修改已经存在的环境变量的值。

int putenv(const char *string);
  1. 使用全新的环境变量,就使用execle()函数,那么替换后的代码切换后的环境变量就只是我们传入的表里的内容

也可以调用其他语言的程序

code.c里:

int main()
{char* const env[] = {(char*)"first",(char*)"second",NULL };pid_t id = fork();if (id == 0){printf("I'm a process, pid: %d\n", getpid());printf("execl begin...\n");execle("./mytest", "mytest", NULL, env)printf("execl end...\n");exit(1);}pid_t rid = waitpid(id, NULL, 0);if (rid > 0){printf("wait successfully\n");}return 0;
}

test.cpp里:

#include <iostream>
#include <unistd.h>using namespace std;int main()
{for (int i = 0; environ[i]; i++){printf("env[%d]: %s\n", i, environ[i]);}cout << "This is C++" << endl;return 0;
}

image-20240416203229251

当然我们也能传系统环境变量,但是没必要,这样的话直接默认就行

execle("./mytest", "mytest", NULL, environ)//传入这个全局变量

想要生成两个可执行文件的makefile

.PHONY:all
all:mycode mytestmycode:code.cgcc -o $@ $^
mytest:test.cppg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f mycode mytest
  1. .PHONY:声明一个或多个目标是伪目标(phony targets)通过声明伪目标,你可以确保 make 总是执行相应的命令,而不会因为同名的文件或目录的存在而跳过这些命令
  2. 运行 make 命令时(没有指定具体目标),make 会首先查找 Makefile 中的第一个目标,并尝试构建它。在这个过程中,make 会检查该目标的所有依赖项,并递归地处理这些依赖项,直到所有必要的依赖项都被构建或确认为是最新的
  3. make 工具被调用以构建某个目标时,它会检查该目标的所有依赖项,并根据需要构建这些依赖项。然而,对于 clean 这样的伪目标,它并没有列出任何依赖项,因此其他目标的构建状态不会影响 clean 的执行

今天就到这里啦,感谢大家支持

相关文章:

Linux:进程控制(二.详细讲解进程程序替换)

上次讲了&#xff1a;Linux&#xff1a;进程地址空间、进程控制&#xff08;一.进程创建、进程终止、进程等待&#xff09; 文章目录 1.进程程序替换1.1概念1.2原理1.3使用一个exec 系列函数execl&#xff08;&#xff09;函数结论与细节 2.多进程时的程序替换3.其他几个exec系…...

Elasticsearch8.13.4版本的Docker启动关闭HTTPS

博主环境是&#xff1a; 开发环境&#xff1a;SpringbootElasticSearch客户端对应的starter 2.6.3版本 maven配置 <!-- ElasticSearch --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elas…...

linux 之dma_buf (8)- ION简化版本

一、前言 我们学习了如何使用 alloc_page() 方式来分配内存&#xff0c;但是该驱动只能分配1个PAGE_SIZE。本篇我们将在上一篇的基础上&#xff0c;实现一个简化版的ION驱动&#xff0c;以此来实现任意 size 大小的内存分配。 二、准备 为了和 kernel 标准 ion 驱动兼容&…...

⌈ 传知代码 ⌋ 高速公路车辆速度检测软件

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…...

scrapy 整合 mitm

1.mitm 是什么 MITMproxy 是一个开源的中间人代理&#xff0c;常用于网络流量的拦截、查看和修改。 2.scrapy 整合 mitm步骤 2.1 安装mitm PS F:\studyScrapy\itcastScrapy> pip install mitmproxy2.2 在settings 中配置下载器中间件 # settings.pyDOWNLOADER_MIDDLEWARES…...

linux大文件切割

在一些小众的场景下出现的大文件无法一次性传输 当然我遇到了 &#xff0c;work中6G镜像文件无法一次性刻盘到4.7G大小的盘 split split -b 3G 源大文件 目标文件 #安静等待会生成目标文件名a、b、c......-b <大小>&#xff1a;指定每个输出文件的大小&#xff0c;单位为…...

图像分割模型LViT-- (Language meets Vision Transformer)

参考&#xff1a;LViT&#xff1a;语言与视觉Transformer在医学图像分割-CSDN博客 背景 标注成本过高而无法获得足够高质量标记数据医学文本注释被纳入以弥补图像数据的质量缺陷半监督学习&#xff1a;引导生成质量提高的伪标签医学图像中不同区域之间的边界往往是模糊的&…...

CANDela studio之CDDT与CDD

CDDT有更高的权限&#xff0c;作为模板规范CDD文件。 CDD可修改的内容比CDDT少。 CDDT根据诊断协议提供诊断格式&#xff0c;主要就是分类服务和定义服务&#xff0c;一般是OEM释放&#xff0c;然后由供应商细化成自己零部件的CDD文件。 在这里举个例子&#xff0c;OEM在CDDT…...

Java中的注解(Annotation)是什么?它们有什么用途?

技术难点 在Java中&#xff0c;注解&#xff08;Annotation&#xff09;是一种元数据&#xff08;metadata&#xff09;的形式&#xff0c;用于为Java代码&#xff08;类、方法、变量、参数和包等&#xff09;提供额外的信息。这些信息在运行时可以通过反射机制进行读取和处理…...

【CUDA】Nsight profile驱动的CUDA优化

前置准备 安装NVIDIA Nsight Compute。 安装好后选择使用管理员权限启动下载官方 Demo 代码官方博客Shuffle warp 1. 任务介绍及CPU版本 1.1 任务介绍 任务理解&#xff1a; 有一个 L x M 的矩阵 M 1 M_1 M1​ 对其每行取平均值 得到 V 1 ∈ R L 1 V_1 \in \mathbb{R}^{…...

字符串的拼接

字符串拼接方式1 之前的算术运算符&#xff0c;只是用来数值类型进行数学运算的&#xff0c;而string不存在算术运算符不能计算&#xff0c;但是可以通过号来进行字符串拼接。 string str "123"; //用进行拼接 str str "456"; Console.WriteLine(str)…...

HIVE3.1.3+ZK+Kerberos+Ranger2.4.0高可用集群部署

目录 一、集群规划 二、介质下载 三、基础环境准备 1、解压文件 2、配置环境变量 四、配置zookeeper 1、创建主体 2、修改zoo.cfg 3、新增jaas.conf 4、新增java.env 5、重启ZK 6、验证ZK 五、配置元数据库 六、安装HIVE 1、创建Hiver的kerberso主体 2…...

Android ANR Trace日志阅读分析技巧

什么是Trace日志 Trace日志是指ANR目录下的一份txt文件 adb pull /data/anr/traces.txt Trace日志有什么用 分析应用ANR无响应的问题&#xff0c; Trace怎么用 Cmd line: com.xx ABI: arm Build type: optimized Zygote loaded classes3682 post zygote classes3750 Intern…...

前端Ajax、Axios和Fetch的用法和区别笔记

前端 JavaScript 开发中&#xff0c;进行 HTTP 请求的三种主要方式是 Ajax、Axios 和 Fetch。这三种方式各有优缺点&#xff0c;并且适用于不同的场景。在合适的业务场景下使用&#xff0c;以下是它们的区别和使用举例。 1. Ajax Ajax&#xff08;Asynchronous JavaScript an…...

Android的Framework(TODO)

&#xff08;TODO&#xff09;...

牛客小白月赛94 EF题解

题目描述 注&#xff1a;此版本为本题的hard&#xff08;困难版&#xff09;&#xff0c;与easy&#xff08;简单版&#xff09;唯一的不同之处只有数据范围。 小苯有一个容量为 k 的背包&#xff0c;现在有 n 个物品&#xff0c;每个物品有一个体积 v 和价值 w&#xff0…...

大数据开发面试题【Flink篇】

148、flink架构 flink是一个框架和分布式处理引擎&#xff0c;用于对无界和有界数据流进行有状态计算 特点&#xff1a; 高吞吐和低延迟&#xff1a;每秒数百万个事件&#xff0c;毫秒级延迟 结果的准确性&#xff1a;提供了事件时间和处理时间语义&#xff0c;提供结果的一致…...

Java技术深度解析:高级面试问题与精粹答案(二)

Java 面试问题及答案 1. 什么是Java的垃圾回收机制&#xff1f;它是如何工作的&#xff1f; 答案&#xff1a; Java的垃圾回收机制&#xff08;Garbage Collection&#xff0c;GC&#xff09;是Java运行时环境&#xff08;JRE&#xff09;中的一个功能&#xff0c;用于自动管…...

算数运算符

算术运算符是用于数值类型变量计算的运算符。 它的返回结果是数值。 赋值符号 关键知识点&#xff1a;先看右侧&#xff0c;再看左侧&#xff0c;把右侧的值赋值给左侧的变量。 附上代码&#xff1a; string myName "唐唐"; int myAge 18; float myHeight 177.5…...

闲话 .NET(3):.NET Framework 的缺点

前言 2016 年&#xff0c;微软正式推出 .NET Core 1.0&#xff0c;并在 2019 年全面停止 .NET Framework 的更新。 .NET Core 并不是 .NET Framework 的升级版&#xff0c;而是一个从头开始开发的全新平台&#xff0c;一个跟 .NET Framework 截然不同的开源技术框架。 微软为…...

WPF实现简单的3D图形

简述 Windows 演示基础 &#xff08;WPF&#xff09; 提供了一种功能&#xff0c;用于根据应用程序要求绘制、转换 3D 图形并为其添加动画效果。它不支持完整的3D游戏开发&#xff0c;但在某种程度上&#xff0c;您可以创建3D图形。 通过组合 2D 和 3D 图形&#xff0c;您还可以…...

设计模式之创建型模式---原型模式(ProtoType)

文章目录 概述类图原型模式优缺点优点缺点 代码实现 概述 在有些系统中&#xff0c;往往会存在大量相同或者是相似的对象&#xff0c;比如一个围棋或者象棋程序中的旗子&#xff0c;这些旗子外形都差不多&#xff0c;只是演示或者是上面刻的内容不一样&#xff0c;若此时使用传…...

git命令新建远程仓库

今天记录一下使用git命令新建远程分支的操作&#xff0c;因为公司的代码管理仓库界面没找到新建分支的操作界面&#xff0c;无奈只能通过git命令来新建分支。 1、新建本地分支 首先&#xff0c;你的至少应该已经有了一个master分支&#xff0c;然后你再master分支下面执行下面…...

Defog发布Llama-3-SQLCoder-8B,文本转SQL模型,性能比肩GPT-4,准确率超90%,消费级硬件可运行

前言 在计算语言学领域&#xff0c;将自然语言转化为可执行的SQL查询是一个重要的研究方向。这对于让那些没有编程或SQL语法知识的用户也能轻松访问数据库信息至关重要。Defog团队近日发布了基于Llama-3的SQLCoder-8B模型&#xff0c;它在文本转SQL模型领域取得了显著突破&…...

防刷发送短信验证码接口的五种简单好用方法绝对够用

防刷发送短信验证码接口的五种简单好用方法&#xff0c;绝对够用 前端增加图形验证码&#xff0c;点击发送按钮后增加60s倒计时&#xff0c;60s后才可以再次点击 后端对接口次数校验&#xff0c;60s内同一电话号码只能发送一次 // 生成基于电话号码的重试锁定键 String repeat…...

ubuntu中idea创建spark项目步骤

1.前置条件 ubuntu中已经安装idea,jdk,scala,spark 2.打开idea&#xff0c;新建&#xff0c;选择Maven项目 3.在IDEA中&#xff0c;File-Setting-Plugin&#xff0c;下载Scala插件 4.File-project structure&#xff0c;导入插件 4.1在全局库中&#xff0c;选择导入刚才的sca…...

回文链表(快慢指针解法之在推进过程中反转)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd;抱怨深处黑暗&#xff0c;不如提灯前行…...

深度剖析:为什么 Spring 和 IDEA 都不推荐使用 @Autowired 注解

目录 依赖注入简介 Autowired 注解的优缺点 Spring 和 IDEA 不推荐使用 Autowired 的原因 构造器注入的优势 Autowired 注解的局限性 可读性和可测试性的问题 推荐的替代方案 构造器注入 Setter 注入 Java Config Bean 注解 项目示例&#xff1a;Autowired vs 构造器…...

【接口自动化_05课_Pytest接口自动化简单封装与Logging应用】

一、关键字驱动--设计框架的常用的思路 封装的作用&#xff1a;在编程中&#xff0c;封装一个方法&#xff08;函数&#xff09;主要有以下几个作用&#xff1a;1. **代码重用**&#xff1a;通过封装重复使用的代码到一个方法中&#xff0c;你可以在多个地方调用这个方法而不是…...

信息学奥赛初赛天天练-14-阅读程序-字符数组、唯一分解定理应用

更多资源请关注纽扣编程微信公众号 1 2019 CSP-J 阅读程序1 (程序输入不超过数组或字符串定义的范围&#xff1b;判断题正确填√,错误填&#xff1b;除特殊说明外&#xff0c;判断题1.5分&#xff0c;选择题3分&#xff0c;共计40分) 1 输入的字符串只能由小写字母或大写字母组…...