大家好,我是苏貝,本篇博客带大家了解Linux进程(9)进程控制1,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
目录
- (A)什么是进程程序替换
- (B)原理
- (C)父子进程中子进程程序替换原理
- (D)使用所有的替换方法exec*,并且认识函数参数的含义
- 1. execl
- 2. execv
- 3. execvp
- 4. execlp
- 5. 替换自己写的程序
- 6. execvpe
(A)什么是进程程序替换
先直接看代码和现象,execl函数先不用管,后面会说到
通过结果我们发现,进程在执行完第一个printf函数后,执行的是ls -a -l的命令,而且不再执行第二个printf函数。所以,我们可以看出,execl函数的作用是让进程通过execl函数,执行新的程序。还是不理解,现在让我们来了解进程程序替换的原理
(B)原理
我们自己的代码编译后生成可执行程序,运行程序就变成了进程,此时操作系统就要为进程创建pcb、地址空间、页表,建立各种映射关系。运行程序testexec时,要将对应的代码和数据加载到内存中。
现在执行execl函数,我们知道,进程=内核数据结构+代码和数据,execl函数做的就是将本进程的代码和数据用新进程的代码和数据覆盖,进程的内核数据结构大体不变,只有少部分属性会发生改变。
站在替换进程的角度,就是这个进程被加载到内存中了
execl函数有没有创建新的进程?
没有,它只是用老进程的壳子执行新进程的代码
回到上面的例子,为什么没有打印第二个printf函数?
因为execl函数将本进程的代码和数据用新进程的代码和数据覆盖了
我们来看一下execl函数,我们上面的代码里面有后面的参数,但是没有用变量接收execl函数的返回值,那返回值重要吗?
不重要,我们可以不关心。只要替换成功,原来的代码就被覆盖了,所以我们不会再执行原先代码。但如果替换失败,我们还是会执行原先代码。
(C)父子进程中子进程程序替换原理
创建子进程,子进程可完成2种任务:
- 子进程执行父进程代码的一部分(即执行fork之后父子进程共享的代码)
- 子进程执行一个全新的程序(使用进程程序替换)
下面来讲父子进程中子进程程序替换的原理
在创建子进程后,父子进程页表映射的是同一块数据和代码空间
子进程要进程程序替换,对于数据a,因为进程程序替换是需要将进程的代码和数据用新进程的代码和数据覆盖,所以相当于要对数据进行写入,为了保证进程的独立性,会发生写时拷贝,即在物理内存中开辟一块新的空间,将数据a拷贝到该空间,再对该空间的数据进行写入(用新进程的数据覆盖),子进程的页表映射到该空间
那代码呢?我们之前讲的都是父子进程的代码共享,那现在呢?
因为子进程的代码也会被替换,所以也会发生写时拷贝
现在来用代码试一试
子进程执行的确实是ls -a -l命令,我们上面说过,只要替换成功,原来的代码就被覆盖了,所以我们不会再执行exit函数,所以退出码是0。
现在我们让进程程序替换失败,只修改了框框里的内容
替换失败,我们还是会执行原先代码exit(2),所以退出码是2
(D)使用所有的替换方法exec*,并且认识函数参数的含义
一共有6种替换方法,下面来逐一介绍
1. execl
exec后面的l:列表
第一个参数path:我们要执行的程序的路径(怎么找到程序,你得告诉我)
后面的参数:在命令行中怎么执行,就怎么传参
最后的参数:一定是NULL
举例:修改.c文件,如果我们想执行ls -l,先传ls的路径/usr/bin/ls,再传我们想怎么执行:在命令行中是ls -l,变成execl函数的参数时,就直接填上去就是了,不过它们要分成2个参数。如果我们想执行的是ls -a -l,那么就要分成3个参数(”ls”,”-a”和”-l”)
2. execv
v:表示数组
第一个参数和execl函数一样,都是要执行的程序的路径(怎么找到程序,你得告诉我)
第二个参数是个char* const类型的数组,只需要将execl除第一个参数以外的所有参数(包括NULL)放入一个数组中,再传这个数组即可
3. execvp
v:需要数组
p:查找这个程序,系统会自动在环境变量PATH中进行查找。所以用户可以不传要执行的程序的路径(但是程序名要传),直接告诉exec*函数,我要执行谁就行
4. execlp
l:list,列表
p:查找这个程序,系统会自动在环境变量PATH中进行查找。所以用户可以不传要执行的程序的路径(但是程序名要传),直接告诉exec*函数,我要执行谁就行
虽然有2个“top”,但它们的含义不同。第一个表示我要执行的程序的程序名,第二个是我要怎么执行该程序
5. 替换自己写的程序
我们上面替换的都是系统命令,可不可以替换我们自己写的程序呢?当然可以
先写要替换的我们自己写的程序的.c文件
修改要被替换的.c文件
execl函数的第一个参数传./myprocess我能理解,就是传要执行的程序的路径。那第二个参数不是说命令行中怎么传,我们就怎么传吗?为什么不是和命令行一样,传./myprocess,而是直接传myprocess?因为我们已经知道了myprocess的路径,命令行中传./myprocess是因为我们不知道myprocess的路径
只用make将2个.c文件都编译形成可执行程序
替换成功
6. execvpe
v:数组
p:查找这个程序,系统会自动在环境变量PATH中进行查找。所以用户可以不传要执行的程序的路径(但是程序名要传),直接告诉exec*函数,我要执行谁就行
e:环境变量
修改被替换进程对应的.c文件
第一个参数按标准来是只需要输入程序名,系统会自动在环境变量PATH中进行查找。但是因为myprocess程序不在PATH内容的路径中,所以只能./myprocess。 第2和第3个参数表示我们将它们传给myprocess程序
修改要替换的我们自己写的程序的.c文件。程序接收到了execvpe函数传递的命令行参数(argv数组)和环境变量(envp数组),现在我们将这两个数组打印出来
了解了execvpe函数之后,我们就可以知道,之前我们说环境变量的时候说bash会将命令行参数表和环境变量的表交给子进程,怎么交呢?就是bash用execvpe函数,将命令行参数表和环境变量表分别作为第二个和第三个参数 传给子进程。子进程将这2张表传给它的子进程同理。
对于execvpe函数的第三个参数的作用:整体替换所有的环境变量。所以传参时有3种选择:
- 用自定义环境变量传参
- 用默认的环境变量传参
- 默认的环境变量稍微修改,再传参
介绍第二种,用默认的环境变量传参
需要用到environ
介绍第三种,默认的环境变量稍微修改,再传参
需要用到putenv函数:修改或添加一个环境变量,其实就是将修改写入环境变量表或写入一个新的变量到环境变量表中
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️