这篇主要说一下Linux下的进程控制中最后一部分内容:进程程序替换。
文章目录
- 1. 进程程序替换
- 1.1 为什么要进程程序替换
- 1.2 替换原理
- 1.3 如何进行程序替换
- 1.3.1 execl函数
- 1.3.2 引入子进程的程序替换
- 1.3.3 execv函数
- 1.3.4 execlp函数和execvp函数
- 1.3.5 如何执行其它语言的程序
- 1.3.6 execle函数
- 1.3.7 execve函数和execvpe函数
1. 进程程序替换
1.1 为什么要进程程序替换
我们知道:子进程一般执行的是父进程的代码片段。但是如果我们想让创建出来的子进程去执行其它的程序呢?我们就需要进程程序替换!!!
我们一般在服务器设计(Linux编程)的时候,往往需要子进程干两件种类的事:
1.让子进程执行父进程的代码片段(服务器代码)
2.让子进程执行磁盘中一个全新的程序(shell,想让客户端执行对应的程序,通过我们的进程执行其他人写的进程代码等等)。
1.2 替换原理
这是我们父进程在运行时,把磁盘中可执行程序中的代码和数据加载到内存中。当我们父进程fork后。子进程的PCB和虚拟内存以父进程为模板,代码共享父进程的,数据也以写时拷贝的方式共享。
此时,在磁盘中有个新的程序,让子进程数据和代码不在和父进程共享。让子进程去执行这个新文件。这样子进程的页表会进行更新指向新的代码段和数据段。
替换原理:
1.将磁盘中的程序,加载入内存结构。
2.重新建立页表映射,谁执行程序替换,就重新建立谁的映射。
效果:让父子进程彻底分离,并让子进程执行一个全新的程序。在这里也并没有产生新的进程。
1.3 如何进行程序替换
因为我们是从磁盘加载到内存,一个硬件把数据转移到另外一个硬件中,这个工作是由操作系统完成的,所以我们需要一个系统函数来完成。在Linux云服务器上由7个程序替换函数。
1.3.1 execl函数
我们先来看一个最简单的:
这三个点叫做可变参数:它的作用是按照用户的意愿传入大小或者数量不等的各个参数。
那么,如果我们想执行一个全新的程序,我们需要做几件事情呢?
1. 先找到这个程序在哪里?
2. 程序是怎么执行的?是带选项执行,还是不带选项执行?
那么,这个execl函数第一个参数的意思是:就是传入程序的路径,第二个传入的就是你程序是如何来执行的(命令行怎么写,这个参数就怎么填),最后必须是NULL,标识如何执行程序的参数传递完毕。
现在我们写一个最简单的例子:
在OS中,命令也是一个程序,我们来运行一下:
我们可以从结果看到:它这里执行了ls -a -l这个命令。我们再来看一个:
这个不带选项的。我们运行一下:
它也执行了pwd这个命令。但是大家有没有发现一个问题:它第二个打印没有进行。
原因是:一旦替换成功,是将当前的代码和数据全部替换了。所以,后面的printf已经被替换了,该代码已经不存在了。
那么我们看这个函数的返回值有什么用呢?要不要判断返回值呢?
答案是:不需要。因为当替换成功了,就不会有返回值了,它就执行其它的代码了,你的返回值没有任何作用。而失败的时候,必然会继续向后执行,最多通过得到什么原因导致的替换失败!!!
我们可以用一个失败的来运行一下:
这里我们是没有这个命令的。
1.3.2 引入子进程的程序替换
看下面的代码:
运行一下:
子进程进行程序替换,会不会影响父进程?
答案是:不会的。因为进程的独立性。
那么它是如何做到的呢?
原因是:当程序替换时,我们可以理解为:代码和数据都发生了写时拷贝完成父子的分离。
1.3.3 execv函数
这个函数也是程序替换函数。第一个参数是:程序的路径。第二个参数:如何执行。
它和execv的区别就是它是一个指针数组。execl是一个一个传参数,而这个是把参数放到数组里,然后传这个数组。
举个例子:
运行结果如下:
1.3.4 execlp函数和execvp函数
这里的第一个参数有什么含义呢?它和execl有什么区别呢?
它第一个参数传的是你要执行什么程序,但是execl需要我们写路径,而这个execlp不需要我们写路径,直接写程序名就行了。因为它是默认在PATH下去寻找的。
举个例子:
我们来看一下运行结果:
那么execvp也是一样的道理,这里就不多说了。
1.3.5 如何执行其它语言的程序
目前我们执行的都是系统命令,如果要执行自己写的C/C++程序,或者执行其它语言写的程序呢?
我们写一个C++的程序:
我们要形成两个可执行程序,让其中一个可执行程序去执行另外一个可执行程序:
但是我们来运行一下:
但是我们只能执行第一个,因为makefile默认执行一个可执行程序。我们需要这样写:
这样我们就可以一下形成两个可执行程序:
然后看一下我们写的程序:
这里用相对路径也可以,只要能找到它就行了:
然后,我们看一下运行结果:
成功的用myexec执行了mycmd。
1.3.6 execle函数
这里的第三个参数是:环境变量的意思。
举个例子:
这里我们写了一个系统里的环境变量PATH,和一个我们自己写的环境变量MYPATH(系统里没有)。
这样就是我们自己写了一个环境变量,然后调用这个函数时导入。然后我们看一下运行结果:
我们看到什么都没有打印,我们把PATH先注释掉:
然后看运行结果:
我们看到它把MYPATH打印出来了。原因是:添加环境变量给目标进程,是覆盖式的。
那么我们该如何解决这个问题呢?
我们这里使用环境变量的指针来传入。
因为系统里没有MYPATH环境变量,所以我们要export设置一下。
这样就都能打印出来了。
1.3.7 execve函数和execvpe函数
那么execvpe我就不多说了,就是把上面的结合起来就行了。
那么现在就出现一个问题:为什么execve是单独的?
原因是:execve是系统接口,而其余6个是对系统接口的封装。这样做的原因就是适配各种应用场景。
这些函数原型看起来很容易混,但只要掌握了规律就很好记: