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 mytest

mycode:code.c
	gcc -o $@ $^
mytest:test.cpp
	g++ -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 的执行

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-24.3,4 SPI驱动实验-I.MX6U SPI 寄存器

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

Vue2基本创建项目

简单版项目初始化 新建一个vue2 官网文档&#xff1a;介绍 — Vue.js 先确保下载了vue的脚手架 npm install -g vue-cli npm install -g vue/cli --force vue -V 创建项目 vue create 自己起个名字 选择自己选择特性 选择&#xff1a; Babel&#xff1a;他可以将我们写…

基于模糊PID控制器的汽车电磁悬架控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于模糊PID控制器的汽车电磁悬架控制系统simulink建模与仿真。 2.系统仿真结果 上面的仿真结果是无控制器和LQG的对比&#xff0c;以及有控制器和LQG的对比仿真。 3.核心程…

UE5 UE4 快速定位节点位置

在材质面板中&#xff0c;找到之前写的一个节点&#xff0c;想要修改&#xff0c;但是当时写的比较多&#xff0c;想要快速定位到节点位置. 在面板下方的 Find Results面板中&#xff0c;输入所需节点&#xff0c;找结果后双击&#xff0c;就定位到该节点处。 同理&#xff0c;…

从0开始学会做标书:新手学习做标书制作必修(95节课)

入门框架 电子标书 商务标书 文档排版 技术标书 实操演示 你是否也有同样的问题 1、做标书公司没人教、没人带? 2、如何看懂招标文件? 3、小白零基础能不能学习做标书? 4、商务标、技术标如何得高分? 5、做标书需要什么软件? 6、如何制作电子标书? 7、如何避…

AI菜鸟向前飞 — LangChain系列之十五 - Agent系列:从现象看机制(中篇)一个Agent的“旅行”

Agent基本架构 先谈谈Agent基本架构概念&#xff0c;如果看得云里雾里&#xff0c;等看完本篇之后&#xff0c;再回头看就会豁然开朗的&#xff0c;而我尽量写得更易懂&#xff1a; &#xff09; 这里面会穿插着上一篇的内容&#xff0c;请大家记得往回翻翻&#xff0c;传送门&…

软件性能测试有哪些测试类型和方法?

软件性能测试是一种通过模拟真实用户使用情况&#xff0c;评估软件系统在各种压力和负载下的表现的测试方法。在今天这个讲究效率的时代&#xff0c;软件性能测试是不可或缺的一环。它能帮助开发人员和企业发现潜在的性能问题&#xff0c;提前优化改进&#xff0c;保证软件系统…

一些关于深度聚类以及部分对比学习的论文阅读笔记

目录 资料SwAV问题方法方法的创新点为什么有效有什么可以借鉴的地方聚类Multi-crop 代码 PCL代码 Feature Alignment and Uniformity for Test Time Adaptation代码 SimSiam 资料 深度聚类算法研究综述(很赞&#xff0c;从聚类方法和深度学习方法两个方面进行了总结&#xff0…

OSINT 与心理学:通过开源情报进行剖析和行为分析

在不断发展的心理学领域&#xff0c;人们越来越认识到通过应用开源情报 (OSINT) 方法取得进步的潜力。OSINT 主要以其在安全和情报领域的应用而闻名&#xff0c;并且越来越多地展示其在心理分析和行为分析方面的潜力。本文探讨了 OSINT 和心理学的迷人交叉点&#xff0c;研究如…

JUC笔记

1、什么是 JUC JUC就是 java.util 下的工具包、包、分类等。 普通的线程代码&#xff1a; ThreadRunnable 没有返回值、效率相比入 Callable 相对较低&#xff01;Callable 有返回值&#xff01; 2、线程和进程 线程、进程&#xff0c;如果不能使用一句话说出来的技术&#x…

Firefox浏览器网页上的按钮点击无效解决办法

我在github下点下载经常不好使&#xff0c;查了原因&#xff0c;原来是浏览器的问题。在Firefox浏览器的设置里面&#xff0c;去掉一些cookies的禁用即可。之后&#xff0c;就可以点击按钮成功响应了。

让大模型更聪明——复杂而艰巨的任务

一、引言 在人工智能领域&#xff0c;大模型因其强大的数据处理能力和复杂的结构&#xff0c;成为了推动技术进步的重要力量。然而&#xff0c;要让大模型真正展现出“聪明”的特质&#xff0c;即具备高度的人类智能水平&#xff0c;仍是一项极具挑战性的任务。本文将从数据质…

Clickhouse Bitmap 类型操作总结—— Clickhouse 基础篇(四)

文章目录 创建 Bitmap 对象Bitmap 转换为整数数组计算总数&#xff08;去重&#xff09;值指定start, end 索引生成子 Bitmap指定 start 索引和数量限制生成子 Bitmap指定偏移量生成子 Bitmap是否包含指定元素两个 Bitmap 是否存在相同元素一个是否为另一个 Bitmap 的子集求最小…

[机缘参悟-191] - 《道家-水木然人间清醒1》读书笔记 -14- 关系界限 - 经济和人格上的独立,走向成熟的必经之路,才能更好的谈其他情感(IT)

目录 前言&#xff1a; 1、“友善的孤独者” 2、“外向的孤独者” 3、道不同不相为谋 4、警惕依赖 5、完整独立的个体 6、不必纠正他人的错误&#xff0c;除非他影响了你 7、不再期待别人能理解自己&#xff0c;只有高维向下兼容你的人才能理解你 8、只有高维和同频的…

简单得阴影引导实现

效果如下: 实现方式&#xff1a; 1、引入三方库&#xff1a; implementation io.github.razerdp:BasePopup:3.2.0 2、代码实现 class NewUserGuide3Popup : BasePopupWindow {constructor(activity: Activity) : super(activity)constructor(context: Context) : super(con…

【ArcGIS For JS】前端geojson渲染行政区划图层并加标签

原理 通过DataV工具 生成行政区的geojson&#xff08;得到各区的面元素数据&#xff09;, 随后使用手动绘制featureLayer与Label&#xff0c;并加载到地图。 //vue3加载geojson数据public/geojson/pt.json,在MapView渲染上加载geojson数据 type是"MultiPolygon"fetc…

K-means 聚类模型详解

K-means 聚类模型详解 K-means 是一种常用的无监督学习算法&#xff0c;用于将数据集分成 K 个簇。其目标是最小化各个簇内数据点到簇中心的距离平方和。K-means 广泛应用于图像压缩、市场细分、模式识别等领域。 算法步骤 初始化: 随机选择 K 个初始簇中心&#xff08;质心…

APM2.8内置罗盘校准

如果你有外置罗盘&#xff0c;可以不用校准内置罗盘&#xff0c;可以忽略此文。推荐使用外置罗盘&#xff0c;内置罗盘容易受干扰。 使用内置罗盘需要插入飞控GPS接口旁边的跳线帽。如图&#xff1a; 如果要使用内置罗盘&#xff0c;而又加了GPS的&#xff0c;记得一定要把GPS…

美光拟投巨资在日本广岛建DRAM厂,目标2027年底投产

美光科技&#xff08;Micron Technology&#xff09;据日本媒体报道&#xff0c;计划在日本广岛县新建一座DRAM芯片生产工厂&#xff0c;目标最快于2027年底投入运营。这一举措标志着美光在增强其内存芯片生产能力方面的又一重大步伐。 报道称&#xff0c;新工厂的总投资规模预…

【Qt秘籍】[003]-Qt环境变量配置-磨刀不误砍柴工

一、为什么要设置环境变量 &#xff1f;[原因] 配置PATH环境变量的主要用处在于让操作系统能够识别并执行不在当前工作目录下的可执行文件。具体来说&#xff0c;它的作用包括&#xff1a; 命令执行便捷性&#xff1a;当你在命令行输入一个命令&#xff08;如java, python或np…