本篇文章,继续来和大家分享与Linux相关的知识。本篇文章主要涉及的内容为进程替换的原理,进程替换函数的使用以及进程替换的演示

进程替换

我们了解进程替换的思路,如下图

Linux-进程替换_进程替换

单进程版

进程替换会用到函数execl,在man手册第三章

Linux-进程替换_进程替换函数_02

我们先来见见怎么使用什么是程序替换

Linux-进程替换_进程替换函数_03

这不就是我们用的ls指令吗?

Linux-进程替换_进程替换原理_04

我们试试top指令

Linux-进程替换_进程替换函数_05

Linux-进程替换_进程替换_06

通过ls指令和top指令的替换,我们不难发现,两个程序在运行的时候,都执行替换程序前的代码,但没有执行替换程序后面部分的代码。这是什么原因呢?我们来了解一下程序替换的原理。

进程替换的原理

通过前面的文章,我们了解,一个进程被创建好后,它会有自己的PCB,在Linux中就是task_stuck。它还会有自己的进程地址空间mm_sturck,和一个页表,页表的中会有一列属性用来存放虚拟地址,在这里列属性的左边会有一列属性用来存放物理地址。物理地址指向进程加载到物理内存中的代码和数据。在物理内存的代码数据只是程序的一部分,程序的所有代码数据都存放在磁盘中,只有再需要的时候,才会从磁盘把代码数据加载到内存中。ls指令,本质也是一个可执行程序,它在磁盘中也存放着它的代码和数据。当调用execl函数时,系统会将ls指令的代码数据加载到内存中,把原有的代码和数据替换掉。这时候,就会有人问了?如果ls指令要加载的代码和数据比较大或比较小怎么办?大了,系统会开辟新的空间,并更改物理地址。小了,系统则会释放掉多余的空间。原来的代码都被替换掉了,也就是没有了。所以,进程替换之后的代码当然不会执行啦,也执行不了。我们可以通过观察,程序执行进程替换之后的代码,来判断进程替换是否成功

Linux-进程替换_进程替换原理_07

进程替换,并不会创建新的进程,我们利用监控脚本来验证,为了便于观察,代码调整为下图

Linux-进程替换_进程替换_08

自程序运行起,只有一个进程9569存在

Linux-进程替换_进程替换_09

进程在替换成功之后,CPU会从头开始执行新的程序。这时,或许会有人问,那CPU怎么知道程序的入口地址?在Linux中,可执行程序文件是有格式的,是ELF格式。该格式的表头会存放不同区域的入口地址。通过表头的入口地址,CPU就可以找到程序的入口地址了。

Linux-进程替换_进程替换函数_10

七个进程替换函数

在理解完原理后,我们可以感觉到进程替换函数,起加载器的效果,把我们需要执行的程序,加载到内存中。进程替换函数一有七个,六个是库函数,一个是系统调用。我们这里以库函数为例,通过库函数讲清楚使用原理,系统调用那个自然也就会了。

Linux-进程替换_进程替换函数_11

我们仔细观察,不能发现这七个函数以exec*开头

第一个execl

Linux-进程替换_进程替换原理_12

execl这个函数带了一个l,l是list的意思。这说明它采用可变参数方式,是像链表节点一样,一个一个的进行传参,最后NULL结尾。下图中的写法,是标准写法。

Linux-进程替换_进程替换_13

大家思考一个问题,我们要执行一个程序第一件事情是什么?当然是先找到这个程序在哪里啦!那找到之后呢?找到之后,要解决的问题是如何去执行这个程序,主要是要不要涵盖选项,涵盖那些?

此时,我们再去理解execl函数,就变得简单了。execl的第一个参数,传绝对路径,我们要告诉execl,要替换的程序在哪里。第二个参数,execl找到这个函数后,你希望它怎么执行。我们平时怎么执行命令的,你就这么传,最后,以NULL结尾就可以了。

第二个execlp

Linux-进程替换_进程替换_14

这个函数比execl多了一个p,p是PATH的意思。什么PATH?是不是跟环境变量PATH很像。它想表达的意思是,你不用像使用execl那样这么麻烦了,不用给我传什么绝对路径,你只需要告诉我,你要替换的程序叫什么就可以了,我会到PATH这个环境变量中的默认路径中去找。

Linux-进程替换_进程替换原理_15

execlp第一个参数,虽然,只传要替换的程序的名字,但本质还是告诉函数这个程序在哪里。第二个参数,我们命令行如何执行命令,这里就这么传,最后以NULL结尾

第三个execv

Linux-进程替换_进程替换函数_16

这个函数带的不是l,而是v。v是vector的意思。可以理解为数组的意思,这里想表达的意思是,你以指针数组的方式告诉我怎么去执行指令。

execv的第一个参数,同样的使用绝对路径告诉我,要替换的程序在哪里。第二个参数传一个以NULL结尾的字符串数组过来,告诉我怎么去执行程序。

Linux-进程替换_进程替换原理_17

第四个execvp

Linux-进程替换_进程替换原理_18

execvp这个函数,相信我不讲你都会。

第一个参数,我们只需要告诉execvp,我们要替换的程序的名字即可

第二个参数,把以NULL结尾的字符串数组传给execvp,告诉它怎么执行这个程序。

Linux-进程替换_进程替换函数_19

第五个execle

Linux-进程替换_进程替换函数_20

execle这个函数,带个e。e就是env环境变量的意思。在这里的意思是,你除了告诉我,程序在哪里,怎么执行,还应该给我传一张环境变量表。

Linux-进程替换_进程替换原理_21

第六个execvpe

Linux-进程替换_进程替换函数_22

execvpe这个函数,带v,带p,带e,我们已经了解它的意思。这里就不多说了。第一个参数,传要替换的程序的名字,第二个参数传字符串数组,第三个参数传环境变量表。

Linux-进程替换_进程替换原理_23

第七个execve

Linux-进程替换_进程替换函数_24

说到这,你如果不会知道怎么用这个函数,建议从第一个开始,再看一遍

Linux-进程替换_进程替换原理_25

Linux-进程替换_进程替换_26

多进程版

execl,execlp,execv,execvp这个四个函数,除了用法有点不同,执行结果都一样,这里我们就以execl演示

Linux-进程替换_进程替换函数_27

我们可以看到子进程进程替换后,后续的代码没有执行。而父进程正常执行,也就是子进程的进程替换并不会影响父进程的运行。这是为什么?父进程创建子进程,它两共享一段代码。之前,我们说过程序一旦启动代码就不能修改,其实,这个得看是谁,如果是你肯定会报错。但操作系统来改,就不会,在它要修改时,发现这段代码父子共享。于是,发生写时拷贝,为子进程拷贝一份代码后,才进行进程替换。

Linux-进程替换_进程替换函数_28

execl,execlp,execv,execvp这四个函数可以执行系统的指令,那它们能不能执行我们的指令,答案是可以的。

我们用C++简单写一个源文件,.cc、.cpp、.cxx都是C++源文件后缀名,只是.cpp比较常用

Linux-进程替换_进程替换原理_29

修改一下execl调用自己的程序

Linux-进程替换_进程替换函数_30

编译运行,就可以把我们自己的程序调用起来了

Linux-进程替换_进程替换函数_31

除了可以调用C++写的程序,还可以调用JAVA和脚本语言写的程序,比如bash,python

我们以bash和python为例

bash,我们需要先创建一个.sh后缀的文件,sh是shell的简写。所谓脚本语言,就是把命令行写的指令放到一个文件里进行执行

Linux-进程替换_进程替换_32

Linux-进程替换_进程替换函数_33

程序一调用,结果就出来了

Linux-进程替换_进程替换_34

再来看看python,这里我们需要创建一个.py为后缀的文件

Linux-进程替换_进程替换_35

Linux-进程替换_进程替换_36

修改完调用,编译后,程序就能跑了

Linux-进程替换_进程替换_37

思考一个问题,无论是我们的可执行程序,还是脚本,为什么可以跨语言跑呢?因为所有语言运行起来,本质都是进程。

ls指令,本质也是个可执行程序,它也会有main函数,execl的第二个参数的内容,实际上是会传给我们要替换的程序的main函数

我们来验证一下

Linux-进程替换_进程替换原理_38

调用的时候,我们故意加上一些选项

Linux-进程替换_进程替换函数_39

编译运行,我们就可以看到我们刚刚传的参数了

Linux-进程替换_进程替换_40

execle,execvpe,execve这三个函数,执行起来的结果和上图一样。我们主要演示另外一个东西,环境变量。

环境变量,我们是熟知的,既是不传,子进程也会继承父进程的环境变量。

Linux-进程替换_进程替换_41

可我们就想传,我们可以使用带e的函数来传环境变量,这里我们以execvpe函数为例,调用otherExe

Linux-进程替换_进程替换_42

在otherExe.cpp中增加打印环境变量信息的功能

Linux-进程替换_进程替换原理_43

编译运行,环境变量的信息就被打印出来了

Linux-进程替换_进程替换函数_44

那我们如何新增环境变量呢?

第一种:直接export增加环境变量

Linux-进程替换_进程替换函数_45

如果你只是想给当前进程增加一个环境变量,则调用putenv函数

Linux-进程替换_进程替换函数_46

Linux-进程替换_进程替换_47

通过grep就可以查到我们增加的环境变量

Linux-进程替换_进程替换_48

我们可以传系统提供的环境变量,那我们可不可以传自己定义的环境变量表,我们试试

Linux-进程替换_进程替换_49

编译运行,我们自定义的环境变量表被打印出来,但原来系统提供的环境变量信息没有了,这里的环境变量的传递,采用的策略是覆盖,而不是追加

Linux-进程替换_进程替换函数_50

Linux-进程替换_进程替换_51

好了,到这里,我们本次的分享就到此结束了,不知道我有没有说明白,给予你一点点收获。关于更多和Linux相关的知识,会在后面的文章更新。如果你有所收获,别忘了给我点个赞,这是对我最好的回馈,当然你也可以在评论发表一下你的收获和心得,亦或者指出我的不足之处。如果喜欢我的分享,别忘了给我点关注噢。