【Linux】进程的程序替换

引语

在Linux中,我们可以通过fork函数创建子进程,然后通过fork的返回值,进行分流,分割开父子进程
那,我们创建子进程的目的是什么呢
是为了让子进程帮我们执行特定的任务

我们知道,子进程会继承父进程的代码和一部分数据,那么这时子进程就可以基于父进程的代码,去完成我们给予的任务
但,子进程也可以指向一个全新的程序代码!这就是进程的程序替换

实例

在这里插入图片描述

我们先使用execl(const charpath,const chararg,… )

参数

参数参数说明
const char *path要调用的程序的绝对路径
const char *arg如何执行程序(命令行操作)最后以NULL结束
可变参数

关于 ... 可变参数
我们在C语言的printf其实用过
在这里插入图片描述
比如我们打印多个数字printf(“%d,%d,%d”,num1,num2,num3);
printf这个函数并不知道我们要传几个参数,所以用…可变参数,代表我们可以传多个参数

代码

那么接下来我们就来实际使用一下execl这个函数
在这里插入图片描述

首先我们打印5行“begin…”,打印“我已经是一个进程啦”,然后调用execl函数,然后在打印5行"end…"
这里说明一下,execl函数的参数

参数参数说明
“/bin/ls”ls命令的绝对路径
“ls”,“-l”,“-a”要执行的操作(这就是可变参数)

PS:注意以NULL表示结尾

运行结果如下
在这里插入图片描述

可以看到,最开始的"begin…"和"我已经是一个子进程啦"成功打印了,但是后续的“end…”并没有执行,而是像我们在命令行敲了一个ls -la一样,展示当前目录的所有文件

原理

我们知道代码和数据是存储在磁盘的,当我们执行时,会被加载到内存。
ls是一个程序,同样拥有代码和数据,也同样存储在磁盘中
最开始我们自己编写的"begin…"和“我已经是一个进程啦”成功被编译,但是我们在之后调用了execl函数,其会发生程序替换
正如下图
在这里插入图片描述
最开始我们正常跑我们自己的代码,有相应的pcb,进程地址空间,页表,但是调用execl后,通过我们指定的程序,会将指定程序从磁盘加载到内存,然后用程序将原来的代码和数据完全覆盖
PS:进程的程序替换,没有创建新的进程。

进程替换的注意点

小知识点

一 .进程的程序替换,没有创建新的进程。

二.程序替换是整体替换,不能局部替换。

三 .进程的程序替换不会影响其他进程。

我们以父子进程举例
在这里插入图片描述
如果子进程的程序替换会影响父进程,那么父进程的内容将不会打印,并且总共有两次ls。
反之,则父进程内容打印,只有一次ls。

运行结果如下
在这里插入图片描述
结果是等待5秒,然后父进程进行打印。并且只有一次ls
说明子进程的程序替换并没有影响父进程

原因
因为当子进程进行程序替换时,会进行写时拷贝。使得父进程和子进程拥有不一样的进程地址空间和页表

execl函数会调用失败吗?

比如我们尝试调用“lsssss”
在这里插入图片描述

运行结果如下

在这里插入图片描述

打印了1次“我是子进程”,2次“我是父进程”,返回值是-1
结论:execl函数如果替换成功,不会有返回值,如果替换失败,一定会有返回
所以只要有返回值,就代表调用失败==>所以我们不需要接收返回值

调用失败,就不会程序替换,会执行原来的代码,所以总共打印2次“我是父进程”

应用

因为如果execl函数调用失败,则一定会有返回值,并且继续执行原来的代码
所以我们可以再execl函数后直接调用exit函数,然后让父进程接收返回值,以此就可以判断是否成功程序替换
在这里插入图片描述

运行结果如下

在这里插入图片描述
父进程成功获取到了子进程exit(1)的退出码。以此判断execl是否调用成功

程序替换函数

以上,我们简单的见识了一下如何程序替换
接下来我们要较为详细的学习所有的程序替换函数

execl

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

执行一个程序,首先第一步:我们要找到他,第二步:我们要确定如何执行他。
这就对应了execl的两个参数

在这里插入图片描述

execv

execv(const char *path,char * const argv[] )
这处讲解一下以上两个函数的命名规律
execl的如何执行是一个一个传参,像list一样
execv的如何执行是传一个字符指针数组,像vector那样

所以execv的用法如下
在这里插入图片描述
我们需要创建一个字符指针数组,其内容同样是如何执行。然后传入execv的参数

运行结果如下

在这里插入图片描述

execlp

execlp(const char *file,const char *arg,...)
execlp的命名和execl就差一个p,这个p的意思就是,PATH。
调用这个函数,他只会在PATH内部,找是否有我们指定的程序

在这里插入图片描述
不需要写绝对路径,直接是程序的名称就好
如何执行同execl一样,需要一个一个传

execvp

execvp(const char*file,char *const argv[] )
同execl和execlp的差别
execvp就是不需要传绝对路径的execv
在这里插入图片描述

execle

execle(const char*path,char *arg,char *const envp[] )
前两个参数同execl,最后一个参数是传给调用的程序的环境变量表
接下来,我们编写一个C++程序otherproc,然后在myproc里面通过execle调用
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里,我们证明了之前的一个小知识点:程序替换没有创建新进程。因为otherproc显示的pid和myproc的pid相同

然后我们再改写一下otherproc.cpp
我们同时获取一下MYENV和PATH两个环境变量
在这里插入图片描述

运行结果如下

在这里插入图片描述

我们发现原本本应该有的PATH变成了NULL
这是怎么回事呢?
原来程序替换传的环境变量表是覆盖式写入,会将原本的环境变量表完全替代为传入的环境变量表

如果我们要将原本进程的环境变量表传过去呢?
可以用C语言定义的一个常量char**environ
在这里插入图片描述
environ是库里的,使用时需加extern表明是外部定义的量

运行结果如下
在这里插入图片描述
那么如何在代码中,导入新的环境变量呢?
我们可以使用putenv()
在这里插入图片描述
可以将新的环境变量导入环境变量表
在这里插入图片描述
运行结果如下
在这里插入图片描述

execvpe

execvpe(const char* file,char* const argv[],char *const envp[] )
基于我们以上的学习,很容易理解这些参数
file是程序名,argv[]是如何执行的字符指针数组,envp[]是传递的环境变量表

execve

execve(const char*filename,char *const argv[],char *const envp[] )
filename是程序名,argv[]是如何执行的字符指针数组,envp[]是传递的环境变量表
以上的6个是在3号man手册的,但是execve是单独在2号man手册的。这是为什么呢?
在这里插入图片描述
其实,只有execve是操作系统最开始使用的程序替换的函数,之前讲述的6种是对execve进行了封装,使其在不同场景有不一样的效率和适应性。

应用

进程的程序替换不仅限于C程序和C++程序的替换,程序替换是操作系统层面的,对于任何语言和程序都是一样的。
比如我们写一个shell脚本和python的代码

shell脚本
在这里插入图片描述
在这里插入图片描述

python代码
在这里插入图片描述
在这里插入图片描述
然后我们在myproc.c中调用这两个程序
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结束语

进程的程序替换的讲解基本就结束了
如果感到有收获的话不妨支持一下博主
文章如果有不对或者不足的地方,欢迎大佬们指正,补充。感谢大家的阅读,如果感觉博主写的还可以,麻烦点个赞支持一下,阿里嘎多。拜托了,这对我真的很重要~
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值