linux之《进程替换》

问题导入

前几篇博文我们知道了,一个进程可以创建一个子进程来执行父进程的部分代码,这个代码是在父进程里面的,属于父进程的一部分,那如果我们想让子进程来执行一个全新的代码任务,我们又该如何实现呢?下面我们来学习并解决这个问题。

进程替换引入

我们先来看看一段代码,带着疑问来思考答案。

#include<stdio.h>
#include<unistd.h> // execl函数的头文件
int main()
{
    printf("父进程开始...\n");
    printf("父进程开始...\n");
    printf("父进程开始...\n");
    execl("/bin/ls","ls","-al",NULL);
    printf("父进程结束...\n");
    printf("父进程结束...\n");
	printf("父进程结束...\n");
    return 0;
}

在这里插入图片描述
我们清楚地看到,调用完execl函数后下面的“父进程结束…”并没有执行,这是为什么呢?

好的这里让我们先来学习一下execl函数的用法和原理吧。

1. execl()

a. 用法

通过查看手册发现,execl函数的完整形式是int execl(const char *path, const char *arg, ...);

形参path表示路径参数arg表示执行命令...表示可变参数
上述代码中的path是/bin/ls,表示执行文件的路径为/bin/ls
arg 是"ls",表示指令ls
... 是"-al",NULL,表示指令的可选项,还有execl最后都是以NULL结尾的,表示参数结束了。

execl的效果就是自调用execl函数后,该进程后面的代码全被execl函数所调用的程序代码所覆盖。也就是说,后面的printf("父进程结束...\n");全部被替换成了命令ls -al 所执行的代码,这就解释了为什么看不到"父进程结束…",而看到当前目录的内容了。

  • 返回值
    execl既然是函数,那肯定有调用失败的时候,它返回的是一个int型,显然失败的时候返回-1成功则什么都不返回

b. 原理

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

大致原理图:
在这里插入图片描述
好的,现在我们知道了如何执行新的程序任务的办法,其实关于execl函数的知识还有很多,下面就是相关的六个函数。

2. execl的相关函数及关系

在这里插入图片描述
通过查手册,我们观察到与其相关的函数还有五个,分别是execlp,execle, execv,execvp,execvpe。现在让我们简单介绍一下他们的用法。

以下五个函数的用法都一样,仅仅只是形参不同。这里主要介绍一下形参的区别。

下面代码均是问题导入代码中替换execl函数的部分代码。

execlp

int execlp(const char *file, const char *arg, ...);

execlp("ls", "ls", "-al", NULL);
  • 讲解:
    看到形参的名字是file的,这表示这个函数自己会去环境变量里面找路径了,形参file传文件名就行了,所以传个ls

execv

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

char *const file[] = {"ls","-al", NULL};
execv("/bin/ls",file);
  • 讲解
    教大家一个便于理解的技巧,execl中的l是list的意思,而这里的v就代表vector的意思,所以后面是指针数组

execvp

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

char* argv[] = {"ls","-al",NULL};
execvp("ls", argv);
  • 讲解:
    显然这个看函数名就大概能知道其形参的含义了,v表示以数组传入,p表示文件名,是的就是前面的缝合体。

从这里就有点不一样了,为了更好的理解execle的功能,所以我们来看改进后的代码

execle

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

进程替换.c

#include<stdio.h>
#include<unistd.h> // execl函数的头文件

int main()
{
	printf("父进程开始...,pid = %d\n",getpid());
	printf("父进程开始...,pid = %d\n",getpid());
	printf("父进程开始...,pid = %d\n",getpid());
	
	char* myenv[] = {"myenv=hello, man", NULL };// 自定义的环境变量
	execle("./son", "son", NULL, myenv);
	
	printf("父进程结束...\n");
	printf("父进程结束...\n");
	printf("父进程结束...\n");
	return 0;
}

另外一个程序 son.cc

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

int main()
{
	cout << "新任务进行中...pid = " << getpid() <<
	" myenvir : "<< getenv("myenv") << endl;
	return 0;
}

执行效果:

在这里插入图片描述

  • 讲解:
    l表示列表,e表示自定义环境变量,所以前面的形参还是一样的,只是多了一个形参char * const envp[],这个形参就是我们自定义的环境变量,如代码“进程替换.c”中的指针数组myenv的第一个。

execvpe

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

	char* myenv[] = {"myenv=hello, man", NULL };
	char* argv[] = {"son", NULL};
	//execle("./son", "son", NULL, myenv);
	execvpe("./son", argv, myenv);

小改动,效果和上面一样

  • 讲解
    v表示用数组来表示可选参数,p表示函数自己回去环境变量里面找程序文件,e表示自定义环境变量。

对于环境变量,有一点要注意:
自定义的环境变量会覆盖原有进程的环境变量
如图:

  • 单独运行son.cc
    在这里插入图片描述
  • 运行myfile
    在这里插入图片描述

如果不想覆盖当前的环境变量,而是在此基础上添加一个,则可以用函数putenv()

如:putenv(“myenv=666”)
修改代码
在这里插入图片描述在这里插入图片描述

联系

其实,上面我们所学的函数,都是这个函数execve的封装,execve是系统调用,c/c++为了便于我们开发者使用,所以对execve进行了封装,然后获得了上面所学的多个版本。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值