Linux:进程替换

news2024/9/20 16:47:25

什么是进程替换?

我们的可执行程序,在运行起来的时候就上一个进程

一个进程就会有他的内核数据结构+代码和数据

把一个已经成型的进程的代码和数据替换掉,这就叫进程替换

也就是可以通过系统调用把当前进程替换位我们需要的进程

那么替换后,会创建一个新进程吗?

不会,只是在旧进程的壳子执行新进程;替换进程后,之前的代码不会执行,因为已经被替换了

进程怎样替换?

进程替换是需要接口函数的,总不能什么都没有直接替换吧

来看看都有什么:

上图的【int execl(const char *pfin,“const char *arg, ...);】这种后面的省略号,是指参数可变

来举个栗子捏:

test1.c

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

makefile:

test1.out:test1.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f test1.out

解释一下:就是把我们本来的代码通过execl来执行execl引用的程序,成为替换的新进程

替换的过程?

而execl的返回值我们并不关心,因为一旦替换成功,就不会向后继续运行旧进程了

而只要继续运行旧进程,那就一定是替换失败了

所以我们的替换接口函数只有失败返回值,没有成功返回值

替换的过程本质上是把这个新程序加载到内存上

一个程序怎样加载到内存上呢? 

别忘了exec*!它类似一种Linux上的加载函数,做的是从外设拷贝到内存是操作系统的活
 

多进程版的进程替换

将代码改成多进程版就是fork创建子进程,让子进程自己替换,父进程只需要等待就好了

也就是说这里被替换的是子进程捏,父进程还在等待

 test1.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        printf("exec begin\n");
        //进程替换
        execl("/usr/bin/ls", "ls", "-al", NULL);
        printf("exec end\n");
        exit(0);
    }
    int rid = waitpid(id, NULL, WUNTRACED);
 
    if(rid > 0)
    {
        printf("wait success\n");
    }
 
    return 0;
}

makefile:

test1.out:test1.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f test1.out

解释一下:我们在if(rid==0)分支里,先打印了一下,然后进入我们的接口,接口里是我们的ls命令,我们ls命令也是文件,也是C语言写的,我们平时执行命令也是执行该程序,所以可以在接口里替换为ls命令

这样就是替换成功了

我们刚刚说了替换失败会继续执行原进程,来看看吧:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
 
int main()
{
    printf("test1.c ... begin!\n");
 
    pid_t id = fork();
 
    if (id == 0)
    {
        sleep(2);
        execl("/usr/bin/lsss", "lsss", "-l", "-a", NULL);
        printf("替换失败捏");
        exit(1);
    }
 
    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("test1.c ... end!\n");
    return 0;
}

你看,没执行execl里面的程序

以前创建子进程让它完成任务,是让子进程执行父进程代码的一部分,现如今可以让子进程执行一个全新的程序

进程有对应的数据和代码,创建子进程会有新的地址空间和页表;替换本身就是覆盖,子进程在数据部分进行写时拷贝,当执行新程序的时候新程序也要进行写时拷贝,所以此后父子间几乎完全独立(本来数据和代码是继承父进程的,现在是全新的了)

来仔细介绍一下我们的替换接口函数:

这些函数的前四个字母都是exec,后面跟着的:

 p: 表示自动搜索路径e: 表示自己维护环境变量l:表示采用列表传参v:表示采用数组传参

execl

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

l就是list(列表),传参数列表

剩下的...带选项,在命令行中怎样写命令的参数,就怎么写(下图有例子捏)结束时传NULL

路径表明我们想要执行谁后面的选项表示我们想要怎样执行

execv

v是vector的意思,动态数组

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

前面的参数是路径,后面的参数是指针数组,execl的是可变参数,execv的传参需要传指针数组指针数组是自己写的

 execvp
带p的意思就是用户可以不用传要执行文件的路径(但要传文件名)

char *const argv[] = {"ps", "-ef", NULL};
execvp("ps", argv);

那它是怎么做到的呢?

别忘了p是可以自己搜路径,在环境变量里搜路径

execlp
就是采用列表传参,但是不用输路径

execlp("ps", "ps", "-ef", NULL);
execvpe

execvpe
带e的需要自己组装环境变量

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

以替换  "ls -al" 为例,示例一下剩余接口:

//l代表传参数列表,分成一个个字符串传
execl("/usr/bin/ls", "ls", "-al", NULL);
 
//p表示系统会去PATH环境变量中找
execlp("ls", "ls", "-al", NULL);
 
//v代表传数组,把命令分成字符串,放进字符串数组里,一起传
char* argv[] = {"ls", "-al", NULL};
execv("/usr/bin/ls", argv);

 替换自己写的进程

刚刚我们引用了系统的进程,我们也可以引用我们自己写的程序

下面就来替换段C++代码(文件后缀: .cpp , .cc ,.cxx)

来先写一个自己的程序:

#include<iostream>
 
using namespace std;
 
int main()
{
	cout << "hello C++!" << endl;
	cout << "hello C++!" << endl;
	cout << "hello C++!" << endl;
	cout << "hello C++!" << endl;
	return 0;
}

然后再也一个父进程:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
 
int main()
{
	printf("test1.c ...begin!\n");
 
	pid_t id = fork();
	if (id == 0)
	{
		sleep(2);
		execl("./mypragma", "mypragma", NULL);
		exit(1);
	}
 
	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("test1.c ... end!\n");
	return 0;
}

tips:别忘了makefile默认是从上到下形成可执行程序,所以第一个要是主程序,其他的依赖程序依次往下排哦

.PHONY:all
all:testexec mypragma
 
mypragma:mypragma.cc
        g++ -o $@ $^ -std=c++11
 
testexec:testexec.c
        gcc -o $@ $^
.PHONY:clean
clean:
        rm -f testexec mypragma

来看看:

程序替换并未形成新进程,我们来验证一下:

test1.c:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
 
int main()
{
	printf("test1.c ...begin!\n");
 
	pid_t id = fork();
	if (id == 0)
	{ printf("i am a process,pid:%d\n",getpid());
		sleep(2);
		execl("./mypragma", "mypragma", NULL);
		exit(1);
	}
 
	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("test1.c ... end!\n");
	return 0;
}

mypragma.cpp

#include<iostream>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
 
int main()
{
  printf("i am a new process,pid:%d\n",getpid());
  cout << "hello C++!" << endl;
	cout << "hello C++!" << endl;
	cout << "hello C++!" << endl;
	cout << "hello C++!" << endl;
	return 0;
}

看,这两个进程pid是一样的

自己组装环境变量

我们刚刚说环境变量可以自己组装,来引用

看看吧:

test1.c:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
 
int main()
{
	printf("test1.c ...begin!\n");
 
	pid_t id = fork();
	if (id == 0)
	{
		sleep(2);
		char *const argv[] = { (char*)"mypragma",NULL };
		char* const envp[] = { (char*)"LIKE=521",(char*)"LOVE=1314",NULL };
		execvpe("./mypragma", argv, envp);
		exit(1);
	}
 
	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("test1.c ... end!\n");	return 0;
}

mypragma.cpp:

#include<iostream>
 
using namespace std;
 
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 << "hello C++!" << endl;
	cout << "hello C++!" << endl;
	cout << "hello C++!" << endl;
	cout << "hello C++!" << endl;
	return 0;
}

哦?

父进程本身自己的环境变量,来自于父进程的父进程:bash

你也可以用全新的环境变量,也可以用修改后的环境变量:使用putenv()

往进程加环境变量: putenv() ,程序替换不会替换环境变量,我们可以通过带e的接口函数(例如execpe())设置新的环境变量,这会覆盖原本的环境变量。

用法:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()

{

char *p;

if((p = getenv("USER")))

printf("USER =%s\n", p);

putenv("USER=test");

printf("USER+5s\n", getenv("USER"));

}

这些都是:

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

tips:只有execve是真正的系统调用,其它五个函数最终都调用 execve(execve在man手册第2节,其它函数在man手册第3节)

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

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

相关文章

正点原子linux开发板 qt程序交叉编译执行

1.开发板光盘 A-基础资料->5、开发工具->1、交叉编译器->fsl-imx-x11-glibc-x86_64-meta-toolchain-qt5-cortexa7hf-neon-toolchain-4.1.15-2.1.0.sh 拷贝到 Ubuntu 虚拟机 用文件传输系统或者共享文件夹传输到linux虚拟机 用ls -l查看权限&#xff0c;如果是白色的使…

【银河麒麟高级服务器操作系统】实际案例分析,xfsaild占用过高

了解银河麒麟操作系统更多全新产品&#xff0c;请点击访问麒麟软件产品专区&#xff1a;https://product.kylinos.cn 服务器环境及配置 物理机/虚拟机 物理机 处理器&#xff1a; Intel(R) Xeon(R) Silver 4110 CPU 2.10GHz 内存&#xff1a; 65536 MiB (64 GiB) 主板…

HTML5简洁的通用网站模板源码

文章目录 1.设计来源1.1 主界面1.2 模板页面11.3 模板页面21.4 模板页面31.5 模板页面41.6 模板页面5 2.效果和源码2.1 动态效果2.2 源码目录2.3 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/1413235…

【AD24报错】GND contains IO Pin and Power Pin objects 的解决方案

【AD24报错】GND contains IO Pin and Power Pin objects 的解决方案 在原理图设计过程中&#xff0c;在元器件接线引脚位置出现红色波浪线&#xff0c;显示错误GND contains IO Pin and Power Pin objects。 点击错误信息可以直接跳转到相关位置。 经查明&#xff0c;是在元…

【FPGA数字信号处理】- 数字信号处理如何入门?

​数字信号处理&#xff08;Digital Signal Processing&#xff0c;简称DSP&#xff09;是一种利用计算机或专用数字硬件对信号进行处理的技术&#xff0c;在通信、音频、视频、雷达等领域发挥着越来越重要的作用&#xff0c;也是FPGA主要应用领域之一。 本文将详细介绍数字信…

高德地图数据采集器|高德地图数据采集软件_一键导出表格

南斗地图数据采集是一款专业采集百度地图、360地图、高德地图、腾讯地图、必应、google、公司、店铺的手机、座机、地址、坐标等数据信息的软件&#xff0c;它与同类软件相比最显著特点是采集地图更专业、采集速度更快、采集更精准、操作方法更简单。 可以导出地图搜索结果数据…

运维学习————Linux环境下Tomcat的部署

目录 一、环境准备 二、 启动测试 三、访问端口修改 四、部署web项目 1、材料准备 2、部署 2.1、上传war包到webapps目录下 2.2、修改项目配置 2.3、浏览器访问 引申 一、环境准备 tomcat安装包&#xff1a;apache-tomcat-9.0.52 JDK环境&#xff1a; 我使用的y…

【TCP】连接管理:三次握手和四次挥手

连接管理 建立连接&#xff1a;三次握手断开连接&#xff1a;四次挥手 网络中的握手/挥手&#xff0c;就是发送不携带业务数据&#xff08;没有载荷&#xff0c;只有报头&#xff09;的数据包&#xff0c;但是能起到“打招呼”这样的效果。次数就是指网络通信的次数。 建立连…

【数学分析笔记】第2章第2节数列极限(3)

2. 数列极限 2.2 数列极限 2.2.5 数列极限的性质 保序性&#xff1a;【定理2.2.3】 { x n } , { y n } , lim ⁡ n → ∞ x n a , lim ⁡ n → ∞ y n b , a < b \{x_{n}\},\{y_{n}\},\lim\limits_{n\to\infty}x_{n}a,\lim\limits_{n\to\infty}y_{n}b,a<b {xn​},{yn…

Android强大的滚动控件RecyclerView

文章目录 Android强大的滚动控件RecyclerViewRecyclerView的基本用法RecyclerView实现横向滚动RecyclerView实现瀑布流布局RecyclerView**的点击事件 Android强大的滚动控件RecyclerView ListView由于其强大的功能&#xff0c;在过去的Android开发当中可以说是贡献卓越&#x…

C语言 | Leetcode C语言题解之第350题两个数组的交集II

题目&#xff1a; 题解&#xff1a; int cmp(const void* _a, const void* _b) {int *a _a, *b (int*)_b;return *a *b ? 0 : *a > *b ? 1 : -1; }int* intersect(int* nums1, int nums1Size, int* nums2, int nums2Size,int* returnSize) {qsort(nums1, nums1Size, s…

你还在手动刷公众号吗?‌试试 RSS 订阅,‌让信息自动汇聚

微信对于反爬的限制非常严格&#xff0c;之前有很多的方案&#xff0c;最后都是被封杀了。我这里列出一些我知道的、实践过的方案。 ‍ RSSHub 在路由“社交媒体”以及路由“新媒体”上都有很多方案&#xff0c;我基本都试过&#xff0c;但这些平台要么是收费的&#xff0c;…

图像数据处理17

四、形态学图像处理 4.3 开运算与闭运算 4.3.1开运算与闭运算的定义&#xff1a; 开运算&#xff1a;先腐蚀&#xff0c;再膨胀 闭运算&#xff1a;先膨胀&#xff0c;再腐蚀 记忆方法&#xff1a;膨胀&#xff08;胀开&#xff09;所以开运算最后对应的结果是膨胀&#x…

学习GitHub:注册与Linux环境对工作内容进行库分支上传

在这个数字化时代&#xff0c;GitHub已成为开发者们不可或缺的宝藏库。无论你是编程新手还是资深开发者&#xff0c;GitHub都能为你打开一扇通往无限创意与协作的大门。今天&#xff0c;就让我们一起踏上这段探索之旅&#xff0c;从GitHub的注册开始&#xff0c;再到如何在Linu…

blender内置树木的UV

之前听说用这个内置插件可以创建树木 我这边的默认配置出来的树木长这样&#xff0c;不得不想一下&#xff0c;他的uv怎么展&#xff0c;看起来好多树 我尝试看了一眼默认的UV 结果如下 好像每个树枝都已经是平铺的样子了&#xff0c;那么如果需要改的话&#xff0c;就根据…

SpringBoot 整合 Excel 轻松实现数据自由导入导出

01、背景介绍 在实际的业务系统开发过程中&#xff0c;操作 Excel 实现数据的导入导出基本上是个非常常见的需求。 之前&#xff0c;我们有介绍一款非常好用的工具&#xff1a;EasyPoi&#xff0c;有读者提出在数据量大的情况下&#xff0c;EasyPoi 会占用内存大&#xff0c;…

DMS:直接可微的网络搜索方法,最快仅需单卡10分钟 | ICML 2024

Differentiable Model Scaling&#xff08;DMS&#xff09;以直接、完全可微的方式对宽度和深度进行建模&#xff0c;是一种高效且多功能的模型缩放方法。与先前的NAS方法相比具有三个优点&#xff1a;1&#xff09;DMS在搜索方面效率高&#xff0c;易于使用。2&#xff09;DMS…

Javascript嵌套函数的调用

这里其实还是讲讲我对javascript闭包的认识。js中闭包就是以嵌套函数的外表出现的。内容是我自己琢磨的&#xff0c;我估计如果这个问题了解很清楚的话&#xff0c;javascript的闭包就没有问题了。其实这里探讨的是一个非常司空见惯&#xff0c;但是可能很多人没有注意的细节。…

SQL-函数篇

在SQL中&#xff0c;函数是用于执行特定操作的预定义代码块。它们可以帮助我们处理字符串、数值、日期等数据类型。下面是一些常用的SQL函数及其用法。 下面是一些常用的函数&#xff1a; 字符串函数 字符串函数用于操作和处理字符串数据。 -- 将两个字符串连接起来 select c…

爱心动画代码HTML5

这段代码是一个HTML5 Canvas动画&#xff0c;它通过JavaScript创建了一个动态的爱心效果。页面初始化时&#xff0c;首先定义了一些基本设置&#xff0c;如粒子数量、持续时间、速度等。然后&#xff0c;定义了Point和Particle类&#xff0c;用于处理粒子的位置和运动。接着&am…