Linxu 进程替换

进程替换的背景:d579ab3bee0f4019bc04a4ebc3dfb2a2.png

进程的替换我们需要调用execl这个接口,excel在3号手册,属于c库函数接口。

 

 

调用系统命令

execl

87bf184b5d7a41f1973441dcc1efa840.png

 

为了方便理解execl的作用,我们写一个程序:

单进程替换

6deb5c90dbe8495586eaccf8990cb883.png1b0eace512ac42e79cffda3fd93c9a00.png我们发现运行结果是通过c库里的exec接口把系统命令 "ls"调用起来了,并且是ls带参的 ls-a-l-n形式

也就是  用ls替换了原本的程序。

 

替换原理

我们的程序跑起来之后就会变成进程,是进程就有PCB,地址空间,页表,物理内存。

同时ls命令也是一个文件,在磁盘当中。

它有代码和数据组成。

我们调用execl,就把ls的代码数据替换了我们原来的代码,数据,完成了进程替换:

5c488a8975f2425cb206e7b0468744e7.png

 

 

多进程替换

 我们让子进程进程ls替换,父进程以阻塞式等待子进程,等待成功把pid打印出来:

e53dd073fc8442a088728738afd80400.png

f22297ace17a44c297dfe9e79065a345.png

 

观察运行结果发现,子进程仍然没有打印出“进程结束这句话”8e4665149bc3401cb958f5e983a8fd6f.png

另外,父进程等待成功之后打印出来的pid和子进程的pid一样,因此得出结论:子进程没有创建成功。

 

为什么不创建子进程,因为子进程是父进程的拷贝,共享一块物理地址,子进程如果被替换,父进程也要被替换,为了进程的独立性,父进程不能被替换。

 

为什么这句话打印不出来呢?466d81db958f4d4498d29893f1f1d0d4.png

原因:

d68f47f528c943d095a35ee737af6053.png

 证明:

我们故意把execl调用写错,这样调用失败就可以按照源程序走,就可以把源程序的后续显示了:fabcb68c0c2a4d899629373b582c015b.png5e6bff6977734d56872980e4d5d1eb7f.png

 

我们可以把execl的返回值用n接受一下,看一下execl替换失败之后会返回什么:10b716beb51a4c999dd9060c6003d58e.png

9822faf37cfb4cd1aca72ea29fbf0542.png

execl替换失败之后会返回-1:95f94194d5f5484197c74cce30e2c826.png

 

我们利用这一特性可以这样写:执行替换,替换成功原程序就不会再执行,替换失败原程序就exit退出。c62b51d39ed1495d923688cab84c3dfd.png

 

 

execlp

744fb21559184aff8527271b516136e4.pngf07b91954d714f18ae5b515730dba5b9.png为什么要写两个ls,第一个ls是我们要替换的文件名,第二个ls是参数的一部分。

 

execv

efc47773c5b3458a89be4bbf0315c9d6.png

写法:

3997dea3c80a42ccac288f0bc854bf6d.png6ae449fd55244cbcbaee8a0818f9d2d9.png

 

execvp

28ab79f46f154c219bff64bf5d74ee83.png

因为自带path,即系统自己会在环境变量路径下查找,我们直接输要替换的目标文件名):5522df4369954dd195330bebd1740ce3.png

我们也可以这样:f292123804464d90a1409e3036325bd2.png因为argv[0]就是ls这个参数。

 

 

 

execle的引入

a21c58048bb043ddbbb3a10c32c75896.png

先不说execle的用法,先说说父子进程环境变量的继承关系。

 

父子环境变量的继承关系

环境变量具有全局属性,也就是环境变量可以被子进程获取。

我们现在有两个文件,一个myproce,一个mytest。我们让myproce去调用mytest。

此时,myproce的父进程是bash,子进程是mytest。

按照子进程会继承父进程环境变量这一说法,myproce应该继承了bash的环境变量,而mytest继承了myproce的环境变量。

证明

在命令行里写一个环境变量,这个环境变量由bash创建:

238245a68a61459d80190250f9482461.png

 然后我们在mytest里面把环境变量打印出来,然后看看mytest里的环境变量里是否有bash创建的环境变量,如果有,说明继承了:

bb153f70e1b44f5ba8a7617644e42b29.png

4ffc8337e08b40f0bcfc2d11c684b3eb.png

原理

通过地址进程空间,让子进程继承父进程的环境变量和代码数据:

adb15e8cf9e04e70905ce170f49f5dcd.png

环境变量被继承下去是一种默认行为,不受进程替换的影响。

程序替换不会替换新程序的环境变量,只会替换新程序的数据和代码。

execle的使用

 现在我们可以用execle了,目的为了给子进程传递环境变量。a21c58048bb043ddbbb3a10c32c75896.png

回忆一下,myproce是父进程,mytest是子进程。我们可以通过./myproce调用./test。

在myproce里用execle接口调用mytest:43c54805c39549cda971de33272b11b1.png

在mytest里打印环境变量:37678153896a4c3badffeccd533d2bca.png

运行:

继承成功0f3dc70711884b6baf64d27ff5eeac9d.png

同样的,我们创建个环境变量,用./myproce运行,子进程./mytest照样可以继承:
409df228e2cb497c9c06834d1d0dede3.png

 

 

总结

子进程执行时候的环境变量从父进程那里来。

两种方式:1.父进程手动传  2.通过地址进程空间继承环境变量,直接用

如果子进程现在不想继承系统环境变量,想继承我们自己写的环境变量呢?

 

自己写环境变量传给子进程

9acfcb43806a4ff8b82d1c7eeab6b577.png

28d7d9d553f742398e0a6974fdc3d1fc.png

我们上面学的execl,execlp,execv,execle等等最终都会转化为execve:
27b3794425b14f21a002e7b8af944cbf.png

 

 

 









上文验证了execl可以把系统文件调用起来替换我们原来文件,那是否可以调用我们自己写的文件来替换呢?

调用c++

写一个mytest.cc文件,让其输出三行hello word:f38cec829d0d4c1f8676960e8b786878.png

在test6.cc里面调用mytest.cc:af0b6a639b4a4a3fbdbd1954d3cc1902.png

写一个makefile:21e59ee015b6447f8f4955ef6b73770c.png

运行结果:

ffdd347549ee48d5a4b258eb5706b956.png结论:execl不仅可以调用系统文件进行替换,也可以调用我们自己写的程序进行替换。

 

调用脚本

创建一个test.sh文件,在里面写一段脚本:7d80654ebd7d4d1a9ae025f8688a3be2.png

用系统解释器bash解释:

c5f60b0bc5dc4fbb800aa8896fabc5fd.png

输入ll,可以看见t1,t2两个文件已经创建好了:97e1b965f02b45f0b9563428c0227d48.png

但是我们发现他们是没有可执行权限的,我们给它加上权限就可以像正常程序一样执行了:725a3f771d8846378aeeb686ff7d416e.png

我们在test6.cc文件里调用一下这个脚本文件:

8016e0533f0b40b0a2c7c9c436a251b3.pngdac24acdf65e4c6f958345d1c0bbb2f9.png发现就可以用这个脚本替换原来的程序。

 

 

调用Python

7bf0fa632ddd432cb69b8089222d76ee.png

822a32a08621428fa284cf8a344c2372.png

然后我们在test6.cc里调用

28e54c860aeb442db621f63c48cde865.png3fe4f0e718d34d5e8f4a8529aad201e3.png

 

 

 

一个程序的创建,执行,结束流程:

613a141729744c02ad57fe6b9eb377d5.png首先 创建pcb,地址空间,页表,但是页表此时没有映射,也就进程创建好了,但是还没开始执行代码。

现在要执行代码了,系统再调用exec*类似的接口把代码和数据从磁盘中加载进内存里,然后填入页表,完成映射。

 

解疑:

7af682c9abf94419a90f1fdda2d83c06.png

全局变量在被编译时磁盘中就有数据了。

因为我们定义全局变量会赋初始值  这个初始值和全局变量会在磁盘.data区域提前预制好,当代码被加载进内存时就加载进去。

局部变量,堆栈上的变量只有被加载时,变为进程时才会有数据,才会形成堆区,栈区

定义的常量字符串,会被编译进可执行程序里的read only这一块区域,在程序被加载内存时会被加载进代码区和全局数据区之间的只读区域。


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孙鹏宇.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值