【进程替换】自定义程序替换 | 替换函数execle&execvpe | execve

目录

自定义程序替换

Makefile

mypragma.cc☞mypragma

testexec.c☞testexec

test.py&test.sh

execle&execvpe

1.自定义 

testexec.c☞testexec 

mypragma.cc☞mypragma

2.系统

3.系统修改putenv 

execve

替换函数总结 


自定义程序替换

前面我们举例进程程序替换用的都是系统命令,可不可以替换我们自己写的程序呢❓

  • 当然可以
  • 我们以execl为例子,请看下面代码C语言替换C++语言。(C++源文件的后缀.cpp/.cc/.cxx均可)

  • 当然无论是C/C++,Java,shell脚本语言都是可以进程程序替换的
  • shell脚本语言,有自己的解释器bash
  • python语言,自己的解释器python3;Java也是一个半解释语言也有编译器。
  • 所有的脚本语言都是需要一个解释器,解释器本身也是由C++语言所写的二进制文件。(可以形成一个可执行程序形成一个进程)
  • 脚本语言也可以加上可执行权限,在命令行中带路径去执行,本质上也是交给解释器去解释的。
  • 所以解释的过程就相当于进程程序替换的过程。
  • 综上所述,所有语言在Linux底下执行,直接间接变成进程,就是被进程程序替换。所以可以用C语言调用其他语言,因为其他语言写成二进制文件(可执行程序),传参到替换函数中(解释器中),形成了进程被调用执行。

Makefile

  • Makefile在形成可执行程序时,默认从上到下匹配。
  • Makefile只会默认形成遇到的第一个二进制文件的可执行程序。

如果想要一次性形成多个可执行程序❓一次编译形成多个可执行程序❓

  • 定义一个伪目标放到第一个匹配列包含形成的可执行程序
  • Makefile为了完成第一个匹配,就会依次完成下面的匹配
  1. 伪目标有依赖关系,无依赖方法
  2. Makefile从上往下扫描。先all,all无依赖方法,有依赖关系(需要推导)。
  3. 所以Makefile会推导完所有的依赖关系(形成必要的可执行程序去完成依赖关系)
  4. all没有依赖方法不执行
Makefile  
  1 .PHONY:all
  2 all:testexec mypragma
  3 
  4 testexec:testexec.c
  5   gcc -o $@ $^
  6 mypragma:mypragma.cc
  7   g++ -o $@ $^ -std=c++11
  8 .PHONY:clean
  9 clean:
 10   rm -f testexec mypragma 

mypragma.cc☞mypragma

mypragma.cc
  1 #include<iostream>
  2 using namespace std;
  3 int main()
  4 {
  5   cout << "hello C++,I am a C++ pragama !" << endl;
  6   cout << "hello C++,I am a C++ pragama !" << endl;
  7   cout << "hello C++,I am a C++ pragama !" << endl;
  8   cout << "hello C++,I am a C++ pragama !" << endl;
  9   cout << "hello C++,I am a C++ pragama !" << endl;
 10   cout << "hello C++,I am a C++ pragama !" << endl;
 11   return 0;                                                                                              
 12 }

testexec.c☞testexec

 testexec.c 
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h>
  6 
  7 int main()
  8 {
  9   printf("testexec.... begin!\n");
 10   pid_t id = fork();
 11   if(id == 0)
 12   {
 13     //child
 14     sleep(2);
 15     execl("./mypragma","mypragma",NULL);  //❗                                                           
 16     exit(1);
 17   }
 18  //father
 19  int status = 0;
 20  pid_t rid = waitpid(id,&status,0);
 21  if(rid > 0)
 22  {
 23    printf("father wait success!,child exit code: %d\n",WEXITSTATUS(status));
 24  }
 25  printf("testexec... end!\n");
 26  return 0;
 27 }

 【证明并没有创建新的进程】 

test.py&test.sh

execle&execvpe

  • e:environment环境变量
  • int execle(const char *path, const char *arg, ...,char *const envp[ ]);
  • int execvpe(const char *file, char *const argv[ ],char *const envp[ ]);
  • file程序的路径
  • argv怎么执行(命令行参数)
  • envp允许你传递的环境变量
  • envp:
  1. 自定义环境变量(会整体替换系统的环境变量)
  2. bash系统的环境变量(extern char**environ)
  3. 系统的环境变量稍微修改,给子进程(增删)(putenv)
  • putenv
  1. man putenv
  2. 调用函数。谁调用putenv。这个函数就给这个进程导出一个全新的环境变量
  3. 想要在子进程中使用修改系统的环境变量,可以在父进程/子进程任何一个都可以调用putenv函数
  4. 环境变量字符串指针直接传入putenv即可(KV形式)
  5. 相当于添加到了当前的环境变量中。

  • bash有环境变量,也可以获取命令行参数
  • bash创建父进程
  • fork创建子进程
  • 通过exec*等函数将环境变量表和命令行参数表交给可执行程序
  • 子进程运行起来,并使用两张表
  • 程序替换的写法很多,用标准写法即可。

  • 平时使用的指令以及指令选项都是父进程给的,命令行参数和环境变量都是父进程给的
  • 父进程内部有两张表,也可以通过execvpe等替换函数将2张表传给子进程
  • 父可以自定义环境变量和命令行参数表通过exec函数传给子进程
  • 父进程本身就有一批环境变量!!从bash来!!

1.自定义 

testexec.c☞testexec 

testexec.c 
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h>
  6 
  7 int main()
  8 {
  9   printf("testexec.... begin!\n");
 10   pid_t id = fork();
 11   if(id == 0)
 12   {
 13     char *const argv[]=
 14     {
 15       (char*)"mypragma",
 16       NULL
 17     };
 18     char *const envp[]=
 19     {
 20       (char*)"HAHA=11111111111",                                                                         
 21       (char*)"HEHE=22222222222",
 22       NULL
 23     };
 24     //child
 25     printf("child id:%d\n",getpid());
 26     sleep(2);
 27     execvpe("./mypragma",argv,envp);
 28     exit(1);
 29   }                                                                                                      
 30  //father
 31  int status = 0;
 32  pid_t rid = waitpid(id,&status,0);
 33  if(rid > 0)
 34  {
 35    printf("father wait success!,child exit code: %d\n",WEXITSTATUS(status));
 36  }
 37  printf("testexec... end!\n");
 38  return 0;
 39 }    

mypragma.cc☞mypragma

mypragma.cc
    1 #include<iostream>
    2 #include<unistd.h>
    3 using namespace std;
W>  4 int main(int argc,char* argv[],char *env[])
    5 {
    6   int i=0;
    7   cout << "hello C++,I am a C++ pragama !" << getpid() << endl;
    8   cout << "hello C++,I am a C++ pragama !" << getpid() << endl;
    9   cout << "hello C++,I am a C++ pragama !" << getpid() << endl;
   10   cout << "hello C++,I am a C++ pragama !" << getpid() << endl;
   11   cout << "hello C++,I am a C++ pragama !" << getpid() << endl;
   12   cout << "hello C++,I am a C++ pragama !" << getpid() << endl;
   13   printf("-------------------------------------------------\n");
   14   for(;argv[i];i++)
   15   {
   16     printf("argv[%d] : %s\n",i,argv[i]);
   17   }
   18   printf("-------------------------------------------------\n");
   19   for(i = 0;env[i];i++)                                                                                
   20   {
   21     printf("env[%d] : %s\n",i,env[i]);
   22   }
   23   printf("-------------------------------------------------\n");
   24   return 0;
   25 }

2.系统

   25     extern char**environ;
   26     printf("child id:%d\n",getpid());
   27     sleep(2);
   28     execvpe("./mypragma",argv,environ);                                                                
   29     exit(1);

 

3.系统修改putenv 

        if(id == 0)
   12   {
W> 13     putenv("tangsiqi=777777777");
   14     char *const argv[]=
   15     {
   16       (char*)"mypragma",
   17       NULL
   18     };
W> 19     char *const envp[]=                                                                                
   20     {
   21       (char*)"HAHA=11111111111",
   22       (char*)"HEHE=22222222222",
   23       NULL
   24     };
   25     //child
   26     extern char**environ;
   27     printf("child id:%d\n",getpid());
   28     sleep(2);
   29     execvpe("./mypragma",argv,environ);                                                                
   30     exit(1);
   31   }

execve

  • Linux支持的C语言标准GNU封装的库函数
  • man 2 execve 
  • execve是唯一的替换函数中的系统调用接口
  • 形式单一,传参数简单
  • 所有的程序替换库函数底层全部都是由execve封装的
  • 目的:为了支持不同的应用场景。

替换函数总结 

#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
 
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
//系统调用函数
int execve(const char *path, char *const argv[], char *const envp[]);

函数解释

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1
  • 所以exec函数只有出错的返回值而没有成功的返回值。

函数解释:

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量

事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。

  • 磁盘中的可执行程序变成子进程☞父进程是bash☞程序被bash加载内存变成进程这个过程本来也就是进程的程序替换

 exec调用举例如下:

#include <unistd.h>
int main()
{
  char *const argv[] = {"ps", "-ef", NULL};
  char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
  execl("/bin/ps", "ps", "-ef", NULL);
  // 带p的,可以使用环境变量PATH,无需写全路径
  execlp("ps", "ps", "-ef", NULL);
  // 带e的,需要自己组装环境变量
  execle("ps", "ps", "-ef", NULL, envp);
  execv("/bin/ps", argv);
  // 带p的,可以使用环境变量PATH,无需写全路径
  execvp("ps", argv);
  // 带e的,需要自己组装环境变量
  execve("/bin/ps", argv, envp);
  exit(0);
}

🙂感谢大家的阅读,若有错误和不足,欢迎指正。下篇进入基础IO篇。进程基础和进程控制总结和练习题以及具体的自编写shell我们会在本周末实现。

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

唐唐思

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

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

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

打赏作者

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

抵扣说明:

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

余额充值