Linux进程替换

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

文章目录

    • [🌼1. 单进程的程序替换](#🌼1. 单进程的程序替换)
    • [🌻2. 进程替换的原理](#🌻2. 进程替换的原理)
    • [🌴3. 多进程程序替换](#🌴3. 多进程程序替换)
    • [🌳4. 程序替换接口 -- exec*](#🌳4. 程序替换接口 -- exec*)

🌼1. 单进程的程序替换

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

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

cpp 复制代码
#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指令

🌻2. 进程替换的原理

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

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

🌴3. 多进程程序替换

cpp 复制代码
#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;
}

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

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

Tips:

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

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

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

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

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

🌿execl

c 复制代码
int execl(const char *path,const char *arg,...);

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

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

🌿execlp

c 复制代码
int execlp(const char *file,const char *arg,...);

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

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

🌿execv

c 复制代码
int execv(const char *path,char* const argv[]);

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

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

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

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

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

🌿execvp

c 复制代码
int execvp(const char *file, char *const argv[]);

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

🌿execle && execvpe

c 复制代码
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++做一个示范

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

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

makefile 复制代码
.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这些都是可以进行调用的

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

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

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

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

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

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

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

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

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

    如果我们需要自己的环境变量,我们就可以自定义环境变量,不要父进程的环境变量

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

相关推荐
小小工匠3 分钟前
系统安全 - Linux /Docker 安全模型及实践
linux·安全·系统安全
2201_761199041 小时前
nginx 负载均衡1
linux·运维·服务器·nginx·负载均衡
suri ..1 小时前
【Linux】进程第三弹(虚拟地址空间)
linux·运维·服务器
害羞的白菜1 小时前
Nginx基础详解5(nginx集群、四七层的负载均衡、Jmeter工具的使用、实验验证集群的性能与单节点的性能)
linux·运维·笔记·jmeter·nginx·centos·负载均衡
纪伊路上盛名在1 小时前
如何初步部署自己的服务器,达到生信分析的及格线
linux·运维·服务器·python·学习·r语言·github
爱滑雪的码农1 小时前
快速熟悉Nginx
运维·nginx·dubbo
Betty’s Sweet1 小时前
[Linux]:线程(三)
linux·线程·信号量·生产者消费者模型
0DayHP1 小时前
HTB:Bike[WriteUP]
运维·服务器
DieSnowK1 小时前
[C++][第三方库][httplib]详细讲解
服务器·开发语言·c++·http·第三方库·新手向·httplib
程序员南飞3 小时前
ps aux | grep smart_webrtc这条指令代表什么意思
java·linux·ubuntu·webrtc