Linux操作系统进程程序替换

  • 创建子进程的目的
  1. 让子进程执行父进程代码的一部分。子进程的代码其实也算父进程的一部分。让子进程执行父进程磁盘代码中的一部分。
  2. 想让子进程执行父进程的一个全新的程序(需要进程替换的原因)。让子进程想办法,执行磁盘上指定的程序,执行新程序的代码和数据。

执行磁盘上指定的程序,执行新程序的代码和数据。需要用到进程程序替换。

  • 为什么需要进程程序替换?

子进程刚创建时和父进程是一样的,父进程想要让子进程执行一个全新的程序就需要让子进程程序替换。

有有时直接在if(ret==0)的里面直接写要替换的程序也是可以的,但在很多地方,进程替换有很大作用。

替换函数

有6种以exec开头的替换函数,统称exec函数。

让指定的程序加载到内存中,让指定的进程进行执行。

 第一个参数是用来找到程序,如何执行程序由后面的参数决定。即是我们在命令行中如何执行,参数就是这么传递的。

ls  -a  -l就相当于参数。

其中的三个点,代表这个函数参数列表是可变参数列表。参数的个数是可变的。

举个例子,像scanf后面就可以传任意数量的参数。

 注意,最后一个参数必须传NULL.

调用exec并不会创建新进程,所以调用exec前后被替换进程的id并未改变。

先用代码看个例子。

#include<stdio.h>
#include<unistd.h>
int main()
{
  printf("process is running:\n");
  execl("/usr/bin/ls","ls","--color=auto","-a","-l",NULL);
  printf("process is stop\n");
    
}

注意,这里面的第二个printf没有执行,为什么?下面会讲解。

  •  理解原理

程序归根结底都是代码和数据,所以当数据和代码被cpu调度时,这个程序就在运行,当我们实际调用exec时,实际是把可执行程序对应的代码和数据进行程序替换,把代码和数据从磁盘向物理内存中指定的位置加载的过程叫程序替换。指定的位置值的是覆盖自己的代码和数据。

当我们进程替换时有没有创建新进程呢?

没有。这个过程只有磁盘中代码和数据替换物理内存中的代码和数据,没有程序的创建,进程pid,task_struct,mm_struct,页表的虚拟地址都没有变。

为什么第二个printf没有执行?

printf也是代码,在execl之后,execl执行完毕后,代码已经被覆盖,开始执行新的程序的代码了,所以printf就无法执行了。

而只要把execl要调用的程序变成一个实际中没有的,execl函数就会失败,就不会成功替换,之前代码就会被保存下来。

#include<stdio.h>
#include<unistd.h>
int main()
{
  printf("process is running:\n");
  execl("/usr/bin/lsscewe","ls","--color=auto","-a","-l",NULL);
  printf("process is stop\n");
    
}

那么execl为什么没有成功后的返回值呢?

因为一旦成功了,就和接下来的代码无关了,判断返回值就没有意义。所以execl只要有错误返回就可以了,execl只要返回了,就一定是错误的。

我们用perror(execl)打印这个代码的错误原因。

 我们常常把进程程序替换代码放入子进程运行。

 这里进程替换不运行父进程,进程具有独立性。

它是怎么保证替换时不影响父进程的呢?

创建子进程一定会给子进程创建pcb和虚拟内存。

虚拟地址空间和页表保证进程独立性,一旦有执行流想替换本进程代码和数据,就会发生写时拷贝。代码和数据都会发生写时拷贝。

  •  各种exec接口的使用方式

  •  execlp有两个"ls",重复吗? 

不重复,第一个告诉系统要执行谁,第二个告诉系统想怎样执行。


 

 execvp和execv的区别和execl和execlp的区别一样。

 


  •  到目前,实现的都是系统当中有的指令,那么想实现自己的指令该怎么办呢?

接着往下看

Makefile,默认只形成一个可执行程序,是从Makefile中的第一个开始执行。

 解决方法是在前面加如下代码。

.PHONY:all
all:mybin myexec

 下面实现一下用execl调用自己写的程序。

被掉程序mycpp.cc的代码,这是个c++程序。

#include<iostream>
int main()
{
  std::cout<<"hello world"<<std::endl;
  return 0;
}

 调用程序的代码。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
  printf("process is running:\n");
  execl("./mycpp","mycpp",NULL);
  perror("execl");
  printf("process is stop\n");
    
}

结果。 

此时不能用带p的execl,因为环境变量里面没有自己写的程序。

 因为mycpp.cc和test10.c在一个目录,所以第一个参数是./,即在当前目录下寻找。

我们可以使用程序替换调用任何后端语言对应的可执行。



mybin 

 

mybin运行。 

 

myexe程序

 失败

 myexe程序

 

 结果

这里面PATH和PWD都是空的,因为此函数替换的程序的环境变量,需要我们自己在后面参数列表中提供,没有提供,环境变量就不会找到。提供什么环境变量,才会有什么环境变量,没有这个环境变量,也就找不到这个环境变量。

myexe

 注意写extern char**environ

结果

此时environ里面没有自定义的MYENV,所以MYEBV为空。

 

 加上下面的代码

 此环境变量表即是数组指针

结果



 execl的作用是把程序加载到内存中,程序为什么要加载到内存,因为CPU执行程序只能在内存中获得数据。那么如何加载呢?Linux是用exec*系列的接口。这些接口叫加载器。 

所以程序是先加载后执行main的。

main函数也需要被传参,所以上述程序的main函数的参数列表,是exec*中的参数传进去的。

而execl,execlp,execv,execvp并没有传环境变量的参数,它替换的程序却能调用默认的环境变量,它就是通过environ,通过地址空间的方式,让此程序拿到的。


 真正执行程序替换的系统调用接口只有这一个,上面所诉其他的都是这个接口的封装。,为了让我们有更多的选择。这7个exec都是需要自己显示调用的,只是最初操作系统提供的exec接口就是execve,剩下的是C库中的函数。

操作系统提供的函数和C库函数的区别https://blog.csdn.net/yugongbaocc/article/details/127964749?spm=1001.2014.3001.5502

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

南种北李

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

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

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

打赏作者

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

抵扣说明:

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

余额充值