【进程替换】自定义程序替换 | 替换函数execleexecvpe | execve

news2024/12/28 5:52:28

目录

自定义程序替换

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我们会在本周末实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1670007.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PythonStudio 控件使用常用方式(十一)MessageBox

PythonStudio是一个极强的开发Python的IDE工具&#xff0c;它使用的是Delphi的控件&#xff0c;常用的内容是与Delphi一致的。但是相关文档并一定完整。现在我试试能否逐步把它的控件常用用法写一点点&#xff0c;也作为PythonStudio的参考。 MessageBox是一个消息对话框&…

docker八大架构之应用服务集群架构

应用服务集群架构 在之前&#xff0c;一个应用层要负责所有的用户操作&#xff0c;但是有时用户增加后就会导致供不应求的现象&#xff08;单个应用不足以支持海量的并发请求&#xff0c;高并发的时候站点响应变慢&#xff09;&#xff0c;这时就需要增加应用层服务器&#xf…

报表-设计器的使用

1、设计器目录结构 报表设计器以压缩包的方式提供&#xff0c;解压后&#xff0c;目录结构如下&#xff1a; 目录说明&#xff1a; 1、jdk-17&#xff1a;压缩包中自带的windows平台下的jdk17 2、lite-report&#xff1a;报表文件和数据源配置文件的保存位置 3、lite-repor…

无人售货机零售项目ECharts展现(最全!!,文档放最后哦!)

目录 背景 数据表 框架分析 可视化展示销售情况总分析 1、绘制仪表盘展示各特征及其环比增长率&#xff08;仪表盘&#xff09; 1. 销售金额及其环比增长率 2. 订单量及其环比增长率 3. 毛利率及其环比增长率 4.售货机数量及其环比增长率 2、绘制簇状柱状-折线图展示…

Controlnet作者张吕敏又一强力新作IC-Light,对图像进行重打光操作,不要太贴心了!

不论是否玩过AI绘画&#xff0c;光照/光照会影响一张照片的质量及完美度&#xff0c;好的光影对照片起到了画龙点睛的作用&#xff0c;更有助于提升电影质感 而在AI绘画中光影控制仅凭提示词&#xff0c;并不能准确细致满足我们的需要。所以Controlnet作者张吕敏新开发了一个重…

【Pychart】jupyter中pyecharts无法显示问题无法使用/No module named pyecharts

无法显示或No module&#xff0c;一般就是更换python版本后&#xff0c;没有在新的python里安装jupyter&#xff1b;另外原因就是引用方式问题&#xff0c;就是import方式不对&#xff1b;都解决后&#xff0c;有报错没有add&#xff0c;或者str问题。 最后的解决方案竟然是bin…

如何通过iptables配置URL过滤黑名单?

正文共&#xff1a;1555 字 16 图&#xff0c;预估阅读时间&#xff1a;2 分钟 我们前面曾经简单介绍过URL过滤功能&#xff08;URL过滤功能了解一下&#xff1f;&#xff09;&#xff0c;并且以H3C VFW为例简单配置了一下URL过滤功能。 首先回顾一下&#xff0c;URL过滤&#…

Linux线程(三)死锁与线程同步

目录 一、什么是死锁 死锁的四个必要条件 如何避免死锁 避免死锁算法 二、Linux线程同步 三 、条件变量 1、条件变量基本原理 2、条件变量的使用 3、条件变量使用示例 为什么 pthread_cond_wait 需要互斥量? 一、什么是死锁 死锁是计算机科学中的一个概念&#xff0c;…

C++指针和动态内存分配细节,反汇编,面试题05

文章目录 20. 指针 vs 引用21. new vs malloc 20. 指针 vs 引用 指针是实体&#xff0c;占用内存空间&#xff0c;逻辑上独立&#xff1b;引用是别名&#xff0c;与变量共享内存空间&#xff0c;逻辑上不独立。指针定义时可以不初始化&#xff1b;引用定义时必须初始化。指针的…

物理机转换成虚拟机之linux

文章目录 注意事项环境准备网络拓扑VMware Converter开始转换 注意事项 目标系统-vCenter-ESXI-VMwareConverter网络必须是互通的&#xff0c;否则&#xff0c;会卡在1% 环境准备 vCenteresxiVMware Converter 网络拓扑 VMware Converter开始转换

20 分页:较小的表

目录 简单的解决方案&#xff1a;更大的页 混合方法&#xff1a;分页和分段 多级页表 详细的多级示例 超过两级 ​编辑地址转换过程&#xff1a;记住TLB 反向页表 将页表交换到磁盘 之前提到的一个问题&#xff1a;就是页表太大&#xff0c;假设一个 32 位地址空间&…

如何进行资产梳理(信息收集)

前言 渗透测试流程 线路一:渗透测试人员 1.域名收集--(备案收集) 1.1在线收集子域名 1.1.1 站长之家 1.1.2 IP138网站 1.1.3 查子域 1.1.4 RapidDNS 1.1.5 聚名 1.1.6 Crt.sh 1.1.7 googleHack 1.2工具和资产测绘收集子域名 1.2.1 oneforall(最好用,最全面) 1.2.…

火山引擎VeDI:A/B测试平台指标能力升级,助力企业提升精细化运营效率

在数字化浪潮的推动下&#xff0c;数据分析与精细化运营已成为企业提升竞争力的关键。近日&#xff0c;火山引擎A/B测试DataTester完成了指标能力的全面升级&#xff0c;为企业在流量竞争激烈的市场中提供了更强大、更可信的数据支持。 此次升级亮点在于引入了“按某个属性去重…

AI大模型探索之路-训练篇22: ChatGLM3微调实战-从原理到应用的LoRA技术全解

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

令牌桶算法:如何优雅地处理突发流量?

令牌桶算法的介绍 在网络流量控制和请求限流中&#xff0c;令牌桶算法是一种常用的策略。那么&#xff0c;令牌桶算法到底是什么呢&#xff1f;它的工作原理又是怎样的呢&#xff1f;让我们一起来探索一下。 令牌桶算法&#xff0c;顾名思义&#xff0c;就是有一个存放令牌的…

云原生技术解析

云原生的概念 云原生是一种软件架构和部署方法&#xff0c;旨在利用云计算的优势&#xff0c;以更灵活、可扩展和可靠的方式构建和部署应用程序。它主要关注在容器、微服务、自动化和持续交付等方面。 云原生技术是指以云计算作为基础&#xff0c;以平台和工具为依托&#xff0…

大规模 RGB LED灯控系统 Lumos:创新与智能化的融合

灯控系统&#xff1a;创新与智能化的融合 在现代照明技术不断进步的背景下&#xff0c;灯控系统的应用已经从简单的开关控制&#xff0c;发展到能够进行复杂程控操作的智能化管理。我们推出的新一代灯控解决方案&#xff0c;凭借其高度的可配置性和跨平台兼容性&#xff0c;已…

Hadopp入门之基础概念

Hadoop概述 Hadoop是什么 Hadoop是一个由Apache基金会所开发的分布式系统基础架构主要解决海量数据的存储和海量数据的分析计算问题广义上来说&#xff0c;Hadoop通常是指一个更广泛的概念——Hadoop生态圈 Hadoop优势 高可靠性&#xff1a;Hadoop底层维护多个数据副本&…

Linux基础之进程-fork()函数的详解

目录 一、前言 二、fork()函数 2.1 fork()函数的基本概念 2.2 问题一的解答 2.3 问题二的解答 2.4 问题三的解答 2.5 问题四的解答 2.6 问题五的解答 一、前言 在上节内容中我们已经学会了使用我们的getpid()和我们的getppid()去查看我们进程的pid&#xff0c;并且学习到…

通过Mendix Portal管理应用整个生命周期

一、前言 大家常常会听到Mendix是一个统一的平台&#xff0c;怎么理解这个统一平台呢&#xff1f;它指的是帮助企业搭建一个统一的开发平台&#xff0c;管理应用的整个生命周期&#xff0c;之前大家更多地关注在应用开发层面&#xff0c;而开发只是整个生命周期的一环。 从上图…