Linux进程替换详解

在这里插入图片描述

进程替换的核心概念

​进程替换的目的​
让子进程执行父进程的部分代码或全新程序(如/bin/ls)。
避免创建新进程,直接复用原有进程的进程控制块(PCB)和资源。

**​代码与数据替换:**新程序的代码段和数据段覆盖原进程的对应区域,但进程ID(PID)保持不变。
​写时拷贝:
代码段通常为只读,替换时直接覆盖。
数据段通过页表映射实现写时拷贝,避免影响父进程。

执行流程:
替换成功后,原进程后续代码不再执行;若失败,exec函数返回错误码

进程替换的实现原理

​调用流程​

使用fork()创建子进程,子进程调用exec系列函数(如execl、execve)加载新程序。
exec函数通过系统调用execve()(唯一内核级函数)实现,其他函数(如execl)为库函数封装。

​内存管理​

​代码段:新程序的ELF格式代码加载到内存,覆盖原进程代码段。
​数据段:通过页表更新,将新程序数据映射到内存,原数据保留(若未修改)。
​堆栈与堆:新程序的堆栈和堆完全替换,与原进程隔离

进程替换函数详解

Linux提供了6个exec系列函数,六种以exec开头的函数,统称exec函数,核心差异在于参数传递方式:
在这里插入图片描述

#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[]);
int execve(const char *path, char *const argv[], char *const envp[]);

l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量

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

execl函数

原型: int execl(const char *path, const char *arg, …)(以NULL结尾的变参列表)

​参数:需指定程序路径,后续参数为命令行参数(arg[0]通常为程序名)。
​功能:不支持环境变量传递,仅替换代码和数据段。
​示例:execl(“/bin/ls”, “ls”, “-l”,“-a”, NULL)。
在这里插入图片描述

代码示例:

#include<stdio.h>
#include<unistd.h>

int main()
{
	printf("Test_execl...begin\n");
	execl("/usr/bin/ls","ls","-l","-a",NULL);
	printf("Test_execl...end\n");
	return 0;
}

代码运行效果:
在这里插入图片描述
输出:列出当前目录下的文件(类似 ls -l -a 的结果)

代码为什么不执行printf(“Test_execl…end\n”); 这条?
因为代码执行execl(“/usr/bin/ls”,“ls”,“-l”,“-a”,NULL); 只要替换成功了,进程就不会继续往下执行,所以不执行后面的代码。只要继续往后执行,就替换失败了。其中,替换过程没有创建新的进程,而是把原来进程的代码是数据替换了。

创建子进程的方式调用execl函数:
代码示例:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	printf("Test_execl...begin\n");

	pid_t id = fork();
	if(id==0)
	{
		sleep(1);
		execl("/usr/bin/ls","ls","-l","-a",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("Test_execl...end\n");
	return 0;
}

运行效果:
在这里插入图片描述
调用完子进程,用子进程替换,可以让子进程去替换全新的程序,等子进程退出后,继续执行父进程的退出代码。
在这里插入图片描述

execv函数

原型:int execv(const char *path, char *const argv[])
参数:需传递程序路径和参数数组(argv以NULL结尾)。
​功能:参数以数组形式传递,不支持环境变量。
​示例:
char *argv[] = {“ls”, “-l”, NULL};
execv(“/bin/ls”, argv);
在这里插入图片描述

代码示例:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	printf("Test_execl...begin\n");

	pid_t id = fork();
	if(id==0)
	{
		sleep(1);
		char* const argv[] = 
		{
			(char*)"ls",
			(char*)"-l",
			(char*)"-a",
			(char*)"--color",
			NULL
		};
		execv("/usr/bin/ls",argv);
		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("Test_execl...end\n");
	return 0;
}

代码运行效果:
在这里插入图片描述

execvp()函数

原型:int execvp(const char *file, char *const argv[])
参数:仅指定文件名,通过PATH查找,参数以数组形式传递。
​功能:结合execv和execlp的特点。
​示例:
char *argv[] = {“ls”, “-l”, NULL};
execvp(“ls”, argv)
在这里插入图片描述
示例代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	printf("Test_execl...begin\n");

	pid_t id = fork();
	if(id==0)
	{
		sleep(1);
		char* const argv[] = 
		{
			(char*)"ls",
			(char*)"-l",
			(char*)"-a",
			(char*)"--color",
			NULL
		};
		execvp("ls",argv);
		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("Test_execl...end\n");
	return 0;
}

代码运行效果:
在这里插入图片描述

execvpe()函数

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

代码示例:

execvpe.c文件

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	printf("Test_execl...begin\n");

	pid_t id = fork();
	if(id==0)
	{
		sleep(1);
		char* const argv[] = 
		{
			(char*)"mypragma",
			NULL
		};
		char* const envp[] = 
		{
			(char*)"HAHA=111111",
			(char*)"HEHE=222222",
			NULL
		}
		execvpe("./mypragma",argv,envp);
	}
	//父进程
	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("Test_execl...end\n");
	return 0;
}

mypragma.cc文件

#include<iostream>
#include<unistd.h>
using namespace std;

int main(int argc, char* argv[],char* env[])
{
	int i = 0;
	for(;argv[i];i++)
	{
		printf("atgv[%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++, I am a C++ pragma! pid:"<<getpid()<<endl;
	cout<<"hello C++, I am a C++ pragma! pid:"<<getpid()<<endl;
	cout<<"hello C++, I am a C++ pragma! pid:"<<getpid()<<endl;
	cout<<"hello C++, I am a C++ pragma! pid:"<<getpid()<<endl;
	return 0;
}

makefile文件:

.PHNOY:all
all:mypragma execvpe

mypragma:mypragma.cc
	g++ -o $@ $^
execvpe:execvpe.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f execvpe

代码运行效果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值