Linux进程替换

28 篇文章 2 订阅

在这里插入图片描述

本章代码gitee仓库:进程替换

🌼1. 单进程的程序替换

在Linux中,存在一批接口,可以进行程序的替换

image-20231030103747971

先多的不说,先来看看猪跑

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
  printf("before:I am process,pid:%d,ppid:%d\n",getpid(),getppid());

  execl("/usr/bin/ls","ls","-a","-l",NULL);

  printf("after:I am process,pid:%d,ppid:%d\n",getpid(),getppid());
  return 0;
}

我们发现,在excel之后的代码,没有执行,而是执行了我们替换的ls指令

image-20231030110045235

🌻2. 进程替换的原理

fork之后创建的子进程会执行父进程的代码接下里的,而调用exec函数会直接替换当前的代码和数据,并不会再创建一个新的进程

当我们调用execl时,会先将/usr/bin/ls目录下的ls加载到内存当中,这个进程就会将自己的代码和数据直接替换为ls的代码和数据,重新开始运行ls

image-20231030162807971

🌴3. 多进程程序替换

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
  pid_t id = fork();
  if(id == 0)
  {
    printf("before:I am process,pid:%d,ppid:%d\n",getpid(),getppid());
    execl("/usr/bin/ls","ls","-a","-l",NULL);
    printf("after:I am process,pid:%d,ppid:%d\n",getpid(),getppid());
    exit(1);
  }
  pid_t ret = waitpid(id,NULL,0);
  if(ret>0) printf("wait success,father pid:%d,ret id:%d\n",getpid(),ret); 
  return 0;
}

运行发现,子进程的程序替换并不影响父进程的运行,这是因为写时拷贝不仅将数据进行了写时拷贝,同时也将代码进行了写时拷贝。

image-20231031090954998

另外,我们看到子进程的先前的进程pid1047,最后父进程回收子进程的时候,回收子进程的pid也是1047,这就能够充分的说明进程替换并不会创建新的进程!

Tips:

我们这里发现,exec*系列的函数,程序替换成功之后,后续的代码不会执行,如果替换失败了,才会执行后续的代码,这就代表着exec*函数,只有失败的返回值,并没有成功的返回值

🌳4. 程序替换接口 – exec*

关于exec*系列的接口一共有7个,他们的开头都说exec

image-20231031104329326

我们目前常用的是man的1、2、3号手册:

  • 1号手册:用户命令手册
  • 2号手册:系统调用
  • 3号手册:C语言库函数

🌿execl

int execl(const char *path,const char *arg,...);

这里的l可以理解成list,这里传参是可变参数,从第二个参数开始,我们在命令行中如何写的,那么我们就如何将参数传给我们的函数,只不过是将空格换成逗号,然后最后一个参数传NULL

image-20231031110432940

我们要执行这个程序,首先第一件事肯定是找到这个程序在哪儿,所以,第一个参数就表示这个程序的路径。

🌿execlp

int execlp(const char *file,const char *arg,...);

这里的p就是我们的path环境变量,我们可以不指定路径,它会直接去PATH环境变量里面找有没有这个可执行程序。

image-20231031112054329

当然了,这里我们第一个参数和第二个参数虽然是一样的,但是含义不一样,第一个表示先找到这个程序,第二个表示如何执行。

🌿execv

int execv(const char *path,char* const argv[]);

这里的v就可以理解为vector,第一次参数同样是表示路径;第二个参数是一个字符指针数组,将命令行参数的地址直接填到这个数组里面。

image-20231101094948750

那这样我们就能将命令行参数填到这个数组里面,然后直接把这个数组进去即可

image-20231101095226931

**Tips:**例如指令ls,它也是一个被编译好的程序,那它也是有main函数的,也有对应的命令行参数,那它这个命令行参数,其实也是从execv的第二个参数传进来的;另外,execl我们已列表的方式传进来,最后也是会被变成char* const argv[这个样子。然后再将这个argv参数传到对应的main函数当中。

在**命令行当中所有的进程都是bash的子进程**,那么所有进程都是采用exec*系列函数进行启动执行的。

所以exec*系列的函数,起到的是一个代码级别的加载器作用,将我们的可执行程序导到内存里面

🌿execvp

int execvp(const char *file, char *const argv[]);

有了前三个函数的例子,我们看到这个p,其实就能猜到,这里的p就表示PATH环境变量。这里就不再过多赘述了

image-20231101100607475

🌿execle && execvpe

int execle(const char *path, const char *arvg, ..., char *envp[]);
int execvpe(const char *file, char *const argv[], char *const envp[])

这里带的e,其实就是表示environment环境变量。

如果exec*系列函数能够执行系统命令,那可否执行我们的自己的程序呢?

我们用C语言程序来调用C++做一个示范

image-20231101105558801

makefile会自顶向下扫描遇到的第一个目标文件是mycommand,所以会执行mycommand的方法,而我们的cppExe和先扫描到的没有任何关联,那么就结束了。

所以我们让第一个目标文件是一个伪目标,然后再建立依赖

.PHONY:all
all:mycommand cppExe
mycommand:mycommand.c
	gcc -o $@ $^ -std=c99
cppExe:cppExe.cpp 
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f mycommand cppExe

这里补充一点:前面讲到,命令行怎么输,那么我们的参数就如何传,可是我们执行cppExe的时候,命令行是./cppExe,而这里传的参数确实cppExe,这里是因为我们命令行中带上./是要告诉bash这个可执行程序在哪儿,而在execl中,第一个参数已经说明了在当前目录,所以第二个参数就不需要再带上./了。

当然了,这也不只是可以调用C++,类似Java、python这些都是可以进行调用的

image-20231101112357879

这些跨语言调用,其实也说明了,所以的语言运行起来,本质上都是进程!

有了上面C语言调用其他语言的例子,我们就能验证,一个程序是否可以给另一个程序传入环境变量参数和命令行参数了。

验证发现,这里传入的命令行参数拿到了,而环境变量压根没有传入,但是也能拿到。

这是因为环境变量也是数据,创建子进程的,环境变量就已经被子进程继承了。

当我们不写这些int argcchar *argv[]char *env[]的时候,也能传进来。

有个extern char **environ第三方变量,它直接指向了环境变量信息,当它被定义的时候,就已经被父进程初始化指向了自己的环境变量表,所以再创建子进程的时候,这个变量就被继承下去了。

image-20231101115612261

同时,我们这里做的是程序替换,而程序替换是直接替换代码和数据,而环境变量并没有被替换掉,所以程序替换中,环境变量信息不会被替换

如果我们想自己手动给子进程传递环境变量,也是可以的,这分为2种:

  1. 新增环境变量
    • 我们可以从shell直接导入环境变量;
      image-20231101121742217
    • 也可以通过父进程创建环境变量导入子进程,采用库函数putenv给进程导入环境变量,当然,我们这里的导入的环境变量和父bash并没有关系。
      image-20231101122444458
  2. 直接替换环境变量
    如果需要直接替换掉,那么就可以用execle或者execvpe,这里采用execle举例子,因为这个更为复杂
    image-20231101124456610
    如果我们需要自己的环境变量,我们就可以自定义环境变量,不要父进程的环境变量
    image-20231101125201001

以上这6个都是C语言封装的库函数,还一个接口,是系统提供的,本质上就是上面这6都都是调用的这个接口:

image-20231101130018453

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

加法器+

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

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

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

打赏作者

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

抵扣说明:

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

余额充值