【Linux系统编程】第二十四弹---从零到一:掌握进程替换的奥秘

news2024/11/18 0:00:24

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、进程程序替换

1.1、替换原理

1.2、替换函数

1.3、函数解释

1.4、命名理解

1.5、代码演示 

1.5.1、execl调用举例

1.5.2、execv 和 execvp 调用举例 

1.5.3、execvpe调用举例 


1、进程程序替换

1.1、替换原理


用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

1.2、替换函数


其实有六种以exec开头的函数,统称exec函数:

#include <unistd.h>
// C语言封装的库函数
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 *filename, char *const argv[],char *const envp[]);

1.3、函数解释

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

1.4、命名理解


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

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

1.5、代码演示 

1.5.1、execl调用举例

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

代码演示一

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

int main()
{
  printf("testexec ... begin!\n");
  execl("/usr/bin/ls","ls","-a","-l","--color",NULL);
  printf("testexec ...  end!\n");
  return 0;
}

运行结果

 代码演示二 

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

int main()
{
  printf("testexec ... begin!\n");
  execl("/usr/bin/lsss","ls","-a","-l","--color",NULL);
  printf("testexec ...  end!\n");
  return 0;
}

运行结果 

多进程版本

fork创建子进程,让子进程自己去替换。

创建子进程,让子进程完成任务:

  • 1、让子进程执行父进程代码的一部分
  • 2、让子进程执行一个全新的程序(替换)

代码演示一(替换系统命令)

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    execl("/usr/bin/ls","ls","-a","-l",NULL);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果 

上面的程序替换,替换的都是系统命令,能否替换自己写的程序呢?

 答案是可以的。

补充:

C++文件后缀名的三种方式:.cpp  /  .cc  /  .cxx

代码演示二(替换自己程序)

testexec.c

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    execl("./mypragma","mypragma",NULL);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

mypragma.cc

#include<iostream>
#include<unistd.h>

using namespace std;

int main()
{
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  return 0;
}

makefile 

此处是自己写的程序,也需要先编译生成可执行程序,下面代码可以使用make一次编译多(两)个文件。

.PHONY:all
all:testexec mypragma

testexec:testexec.c 
	gcc -o $@ $^ -std=c99
mypragma:mypragma.cc 
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -rf testexec mypragma 

运行结果 

上面的程序替换的是C++,能否替换其他语言呢?

答案是可以的,我们替换的是可执行程序,只要是程序即可。 

在替换之前,我们先简单使用其他语言写一个程序!!!

python

#!/usr/bin/python3

print("hello python")
print("hello python")
print("hello python")
print("hello python")

运行结果 

能否像以前一样使用./可执行程序运行程序呢?

 答案是可以的,但是需要加执行的权限。

shell 

#!/usr/bin/bash

cnt=0
while [ $cnt -le 10 ]
do
    echo "hello shell, cnt: ${cnt}"
    let cnt++
done

运行结果 

代码演示

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    //execl("./mypragma","mypragma",NULL);
    execl("/usr/bin/python3","phthon3","testpy",NULL);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果  

1.5.2、execv 和 execvp 调用举例 

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

 代码演示

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    char* const argv[]=
    {
     (char*)"ls",
     (char*)"-a",
     (char*)"-l",
     (char*)"--color",
      NULL
    };
    // execv("/usr/bin/ls",argv);
    execvp("ls",argv);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果 

1.5.3、execvpe调用举例 

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

代码演示:

mypragma.cc

#include<iostream>
#include<unistd.h>

using namespace std;

int main(int argc,char* argv[],char* env[])
{
  int i=0;
  for(;argv[i];i++)
  {
    printf("argv[%d] : %s\n",i,argv[i]);
  }
  printf("-------------------------------\n");
  for(i=0;env[i];i++)
  {
    printf("env[%d] : %s\n",i,env[i]);
  }

  printf("-------------------------------\n");
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  return 0;
}

testexec.c(手写环境变量)

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    char* const argv[]=
    {
      (char*)"mypragma",
      (char*)"-a",
      (char*)"-b",
      NULL 
    };
    char* const envp[]=
    {
      (char*)"HAHA=111111",
      (char*)"HEHE=222222",
      NULL 
    };
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    execvpe("./mypragma",argv,envp);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果 

 testexec.c(系统环境变量+手写)

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>
#include<stdlib.h>

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    // 我的父进程本身就有一批环境变量,从bash来,自己想添加可以使用putenv
    putenv("HHHH=333333333");// 头文件 stdlib.h
    char* const argv[]=
    {
      (char*)"mypragma",
      (char*)"-a",
      (char*)"-b",
      NULL 
    };
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    extern char** environ;
    execvpe("./mypragma",argv,environ);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果 

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

下图exec函数族 一个完整的例子:

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

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

相关文章

线上环境排故思路与方法GC优化策略

前言 这是针对于我之前[博客]的一次整理&#xff0c;因为公司需要一些技术文档的定期整理与分享&#xff0c;我就整理了一下。(https://blog.csdn.net/TT_4419/article/details/141997617?spm1001.2014.3001.5501) 其实&#xff0c;nginx配置 服务故障转移与自动恢复也是可以…

九、子查询

文章目录 0. 引入1. 需求分析与问题解决1.1 实际问题1.2 子查询的基本使用1.3 子查询的分类1.3.1 分类方式11.3.2 分类方式2 2. 单行子查询2.1 单行比较操作符2.2 代码实例2.3 HAVING 中的子查询2.4 CASE中的子查询2.5 子查询中的空值问题2.6 非法使用子查询 3. 多行子查询3.1 …

FPGA实现PCIE图片采集转HDMI输出,基于XDMA中断架构,提供3套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的PCIE方案 3、PCIE基础知识扫描4、工程详细设计方案工程设计原理框图测试图片QT上位机XDMA配置及使用XDMA中断模块FDMA图像缓存Native视频时序生成RGB转HDMI输出模块Windows版本XDMA驱动安装Linux版本XDMA驱动安装工程源码…

awvs工具测试输出漏洞报告教程

1.登录进awvs 2.添加要扫描的网站url 3.这里选择生成新的报告 4.开始扫描 5.生成报告

Spring Boot 核心概念

1. 起步依赖 Spring Boot 提供了一系列以 spring-boot-starter- 为前缀的起步依赖&#xff0c;通过这些依赖&#xff0c;可以快速引入 Web、JPA、Security、Redis 等功能。例如&#xff1a; spring-boot-starter-web&#xff1a;包含 Web 开发常见的依赖&#xff0c;如 Sprin…

TypeScript 设计模式之【备忘录模式】

文章目录 备忘录模式&#xff1a;时光机器的魔法备忘录模式的奥秘备忘录模式有什么利与弊?如何使用备忘录模式来优化你的系统代码实现案例备忘录模式的主要优点备忘录模式的主要缺点备忘录模式的适用场景总结 备忘录模式&#xff1a;时光机器的魔法 想象一下&#xff0c;如果…

调用飞书接口导入供应商bug

1、业务背景 财务这边大部分系统都是供应商项目&#xff0c;由于供应商的研发人员没有飞书项目的权限&#xff0c;涉及到供应商系统需求 财务这边都是通过多维表格进行bug的生命周期管理如图&#xff1a; 但多维表格没有跟飞书项目直接关联&#xff0c;测试组做bug统计的时候无…

【Docker】01-Docker常见指令

1. Docker Docker会下载镜像&#xff0c;运行的时候&#xff0c;创建一个隔离的环境&#xff0c;称为容器。 docker run -d \ # 创建并运行一个容器&#xff0c;-d表示后台运行 --name mysql \ # 容器名称-p 3307:3306 \ # 端口映射&#xff0c;宿主机端口映射到容器端口-e TZ…

打造同城O2O平台:外卖跑腿APP的架构与功能设计详解

今天&#xff0c;小编将于大家共同讨论外卖跑腿APP的架构设计及其核心功能&#xff0c;旨在为开发者提供一份详尽的参考。 一、外卖跑腿APP的架构设计 1.整体架构概述 通常包括前端、后端和数据库。 2.前端设计 用户端提供直观的界面&#xff0c;方便用户下单、查询订单状态…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《基于自适应时段划分的含氢微能网中长期变分辨率调度》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

Rust gRPC---Tonic实战

API 一个API做了两件事 客户端发起请求Request服务端作出响应Response REST是什么 REST&#xff08;Representational State Transfer&#xff09;&#xff1a;表现层状态传输&#xff0c;是一种设计风格&#xff0c;通常将 HTTP API 称为 RESTful API、RESTful 服务或 RES…

AI大模型生态暨算力大会今日举行,20位大咖领衔共探「AI NATIVE,生成未来」

出品&#xff5c;AI大模型工场 9月25日消息&#xff0c;由AI大模型工场主办AI大模型生态暨算力大会今日举行。作为国内最具影响力与最懂大模型的AI生态大会&#xff0c;大会讨论了AI大模型的最新进展和未来发展趋势。 2024年被业内称为大模型应用落地元年&#xff0c;大模型产…

【入门01】arcgis api 4.x 创建地图、添加图层、添加指北针、比例尺、图例、卷帘、图层控制、家控件(附完整源码)

1.效果 2.代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title></title><link rel"s…

JSP+Servlet+Mybatis实现列表显示和批量删除等功能

前言 使用JSP回显用户列表&#xff0c;可以进行批量删除&#xff08;有删除确认步骤&#xff09;&#xff0c;和修改用户数据&#xff08;用户数据回显步骤&#xff09;使用servlet处理传递进来的请求参数&#xff0c;并调用dao处理数据并返回使用mybatis&#xff0c;书写dao层…

828华为云征文|华为云Flexus云服务器X实例的网络性能测试

828华为云征文&#xff5c;华为云Flexus云服务器X实例的网络性能测试 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、iperf3工具介绍2.1 iperf3简介2.2 iperf3特点 三、本次实践介绍3.1 本次实践…

专利如何有效维持?

专利的获得并非一劳永逸&#xff0c;其法律效力的持续存在依赖于有效的专利维持工作。专利维持&#xff0c;即专利权人在专利有效期内&#xff0c;按照法定程序缴纳年费、提交必要的文件&#xff0c;以保持专利权的有效状态。这一过程对于确保创新成果持续闪耀、维护企业竞争优…

DSP——从入门到放弃系列——多核导航器(持续更新)

1、概述 C6678中的数据移动非常复杂&#xff0c;多核导航器是C6678中协助完成在器件内高速数据包移动的外围设备。 2、组成 多核导航器由1个队列管理子系统&#xff08;QMSS&#xff09;1个包DMA&#xff08;Packet DMA PKTDMA&#xff09;来控制和实现器件内的高速数据包移…

Arthas mc(Memory Compiler/内存编译器 )

文章目录 二、命令列表2.2 class/classloader相关命令2.2.2 mc &#xff08;Memory Compiler/内存编译器 &#xff09;举例1&#xff1a;可以通过-d命令指定输出目录&#xff1a;mc -d /temporary/tmp /temporary/tmp/AccountController.java举例2&#xff1a;通过--classLoade…

SpringBoot-全局处理异常,时间格式,跨域,拦截器,监听器

1.全局异常处理 使用ControllerAdvice与ExceptionHandler注解 /*** 全局异常处理程序** author * date */ ControllerAdvice ResponseBody public class GlobalExceptionHandler {ExceptionHandler(Exception.class)public JsonResult handleException(Exception e) {e.print…

Vue3 中 this 一分钟了解

Vue3 中 this 在Vue3的开发过程中&#xff0c;this的使用方式和Vue2有着显著的不同&#xff0c;特别是在组合式API&#xff08;Composition API&#xff09;的引入后。本文将深入探讨Vue3中this的使用&#xff0c;解析其底层源码&#xff0c;并探讨这种设计背后的原因&#xff…