进程控制(三):进程替换

news2024/11/28 10:33:39

文章目录

  • 进程控制(三)
  • 进程替换
    • 进程替换函数
    • 进程中的环境变量
  • 总结


进程控制(三)

进程控制中的进程替换,下文我们学习进程替换的意义,以及进程替换的方式

进程替换

初步认识进程替换,我们先使用一个比较简单的execl函数,来实现进程替换,并从结果上,了解替换的原理。

单进程进程替换

简单实现一下进程替换,以此推理进程替换原理

在这里插入图片描述

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


int main()
{
  //实现单进程的进程替换
  printf("hello linux\n");//输出
  execl("/usr/bin/ls","ls","-a","-l",NULL);//输入的第一个参数为要执行的进程的绝对路径,后面的参数为命令行参数,可变序列参数以NULL结尾,表示结束
  
  printf("hello world\n");
  return 0;
}

单进程实现进程替换,我们发现进程的代码和数据会被替换,替换execl函数内参数所指定的内容。

多进程(父子进程)实现进程替换

在这里插入图片描述

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

int main()
{
  //实现单进程的进程替换
 // printf("hello linux\n");//输出
 // execl("/usr/bin/ls","ls","-a","-l",NULL);//输入的第一个参数为要执行的进程的绝对路径,后面的参数为命令行参数,可变序列参数以NULL结尾,表示结束
 // 
 // printf("hello world\n");
  
  //实现多进程(父子进程)进程替换
  
  //创建子进程
  pid_t id=fork();

  if(id==0)
  {
    //表示子进程
    printf("i am child process pid: %d ppid: %d\n",getpid(),getppid());//得到父进程子进程的pid
    execl("/usr/bin/ls","ls","-l",NULL);//实现程序替换

    exit(11);//退出码,查看是否是会覆盖该代码,如果覆盖的话,我们执行的是ls命令,执行成功,返回码exit为0,通过waitpid进行等到得到status,WEXITSTATUS(status)来判断是否是这样
  }
  else if(id<0)
  {
    //表示子进程创建失败
    perror("fork failed\n");
    exit(1);//表示是创建失败
  }
  else 
  {
    //这是父进程要执行的内容,我们先进行等待
    
    //waitpid等待
    int status=0;
    pid_t ret=waitpid(-1,&status,0);//阻塞等待

    if(ret<0)
    {
      //表示等待失败
      perror("wait failed\n");
      exit(2);
    }
    else 
    {
      //等待成功,我们要输出的是子进程的退出码
      printf("i am parent pid: %d child pid: %d child exit_code: %d\n",getpid(),id,WEXITSTATUS(status));
    }
  }
    return 0;
}

上述多进程进程替换后的结果,表明了进程替换的原理,为将新进程的数据和代码直接覆盖原来的进程的代码。

进程替换原理

  1. 首先,我们知道进程运行时,有task_struct结构体来存储进程信息,以及虚拟空间地址(mm_struct)存储数据,通过页表映射到物理内存
  2. 先运行原有进程,然后fork创建子进程,在子进程中使用进程替换,把子进程的数据和代码进行替换,操作系统识别到要更改子进程的代码和数据,在物理内存中开辟空间,将新进程代码和数据放入,更改页表指向该地址。
  3. 通过上述步骤,实现了子进程的进程替换,此时子进程的task_struct除了代码和数据地址,其他没有变化。

如下图所示:

在这里插入图片描述

通过上述内容,我们就能明白进程替换的原理了,起始就是更改物理空间原有进程的代码数据,如果是父子进程,那么就在物理空间上开辟新空间,存放代码和数据,更改子进程页表指向。

进程替换函数

进程替换函数有7种,其中,库函数(man 3号手册)中有6种函数,系统调用(man 2号手册)有一种函数

在这里插入图片描述

       //man 2 exec   库函数 6种
	   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[]);

下面是这6种函数的使用样例:

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


int main()
{
  //使用进程替换,为了简单的演示,我们就单纯使用单进程进程替换来实现
  //1. int execl(const char *path, const char *arg, ...);
  int num=0;
  scanf("%d",&num);//输入参数选定不同的进程替换参数
  if(num==1)
  {
    printf("进程替换函数execl\n");
    execl("/usr/bin/ls","ls","-l",NULL);//实现完毕,可变列表的实现,以NULL为结尾
    printf("进程替换完毕\n");
  }
  else if(num==2)
  {
    //int execlp(const char *file, const char *arg, ...);
    //该函数的实现,第一个参数,不必要使用绝对路径,会从PATH以及当前目录下,找到可执行文件,并结合avg命令行参数实现
    printf("execlp函数的实现\n");
    execl("/usr/bin/pwd","pwd",NULL);//实现显示当前工作路径
  }
  else if(num==3)
  {
    //int execle(const char *path, const char *arg,  ..., char * const envp[]);
    //实现功能:需要绝对路径,需要可变列表,需要环境变量
    printf("execle函数的实现\n");
    char * const env[]={"why=123",NULL};
    execle("/usr/bin/ls","ls",NULL,env);
  }
  else if(num==4)
  {
    // int execv(const char *path, char *const argv[]);
    printf("execv函数的实现\n");
    char * const argv[]={"top",NULL};
    execv("/usr/bin/top",argv);

  }
  else if(num==5)
  {
    //int execvp(const char *file, char *const argv[]);
    printf("execvp函数的实现\n");
    char * const argv[]={"ls",NULL};
    execvp("ls",argv);
  }
  else if(num==6)
  {
    //int execvpe(const char *file, char *const argv[],  char *const envp[]);
    printf("execvpe函数的实现\n");
    char * const argv[]={"ls",NULL};
    char * const env[]={"lll=123456",NULL}; 
    execvpe("/usr/bin/ls",argv,env);
  }
  return 0;
}

解释:

  1. 对于环境变量以及命令函参数,无论是可变参数形式,还是字符指针数组形式,最后一个元素都要是NULL结尾,标志参数输入完毕。
  2. 对于这六种函数本质上没什么太大的区别,只是对于参数的形式不同。
  3. 关于环境变量,我们可以使用putenv和getenv函数来增加或者得到指定的环境变量,参数都是字符指针类型,比如getenv(“PATH”)

进程中的环境变量

对于进程中的环境变量进行解释,以下图为准

在这里插入图片描述

  1. getenv和putenv函数可以实现得到/创建环境变量信息
  2. PATH环境变量,父子进程都能获得,我们在后文可知,实际上是bash对于进程默认传参environ环境变量(在Linux下就是env命令展示的内容),所以只要系统环境变量中存在的,我们在进程中都能直接getenv获得。
  3. 子进程得到环境变量有两种方式:新增环境变量(putenv,在fork之前),另外是通过进程替换函数,自定义环境变量参数,传参(是彻底替换,即覆盖默认environ)

在这里插入图片描述

通过上述图中代码结果表明,我们putenv新建的环境变量,实际上只是在当前进程中的environ有效,但是等进程结束之后,输入env命令,并不会有我们新增的环境变量,这说明,由于进程的独立性,子进程的环境变量并不会影响其他进程(系统,本身也是一个进程)。

在这里插入图片描述

最后,对于库函数中的6种进程替换函数,以及对于其中环境变量与进程的关系,我们已经解释完毕,最后一个系统函数调用execve,我们只需要知道,前6种库函数,最后底层都是调用该函数,对于操作系统进行操作(也只有系统调用函数才能对于操作系统操作),以此全局视角,才能操控进程,从而替换进程中数据和代码,最终实现进程替换。

总结

  • 进程替换,实际上只是对于该进程的程序和代码进行替换,子进程的其他信息如pid等都不发生变化,如果是存在父子进程的情况,对父子进程进程替换,会在物理空间中在开辟新空间来存放新进程的代码和数据。
  • 掌握7个进程替换函数,其中6个是库函数,一个是系统调用函数,库函数的底层会使用这个系统调用函数,来实现对于系统中的进程进行操作。
  • 进程环境变量是有默认的数值的,即一个进程默认的环境变量为操作系统中的env命令展示的内容,在C语言中,我们将environ(类型为char**)变量指向该环境变量,如果在执行进程替换中传递我们自己创建的环境变量, 如myenv,会覆盖原有的environ环境变量。

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

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

相关文章

Appium —— 初识移动APP自动化测试框架Appium

说到移动APP自动化测试&#xff0c;代表性的测试框架非Appium莫属&#xff0c;从今天开始我们将从APP结构解析、Appium框架学习、安卓/iOS自动化测试实战、自动遍历回归测试、自动化测试平台及持续集成&#xff0c;多个维度一起由浅入深的学废Appium 今天我们先来初步认识Appi…

远程设备常用工具:向日葵、Todesk

其实按理说远程工具例如向日葵、Todesk如果是计算机专业、计算机从业者是必须知道的一个东西&#xff0c;但是在大学期间身边知道的人是少之又少的。 向日葵、Todesk工具的优势&#xff1a;方便、快捷、速度快等等我就不过多阐述了 PS:现在我就是在学校用远程写这篇 很多时候…

使用Navicat访问宝塔中的MySQL数据库

首先放开数据库权限 然后放开3306端口 数据库默认端口是3306&#xff0c;如果改了&#xff0c;这里就填改的那个端口 连接数据库 复制数据库用户名和密码 填入Navicat中&#xff0c;主机只需要填IP即可 连接成功

动手学深度学习——残差网络ResNet(原理解释+代码详解)

残差网络ResNet 1. 函数类2. 残差块3. ResNet模型4. 训练模型 ResNet为了解决“新添加的层如何提升神经网络的性能”&#xff0c;它在2015年的ImageNet图像识别挑战赛夺魁 它深刻影响了后来的深度神经网络的设计&#xff0c;ResNet的被引用量更是达到了19万。 1. 函数类 假…

Android studio新版本多渠道打包配置

最近公司套壳app比较多 功能也都一样只有地址&#xff0c;和app名字还有icon不一样 签名文件也是一样的,所以就研究了多渠道打包 配置如下&#xff1a; 在app下build.gradle配置 因为最新版as中禁用了BuildConfig 所以我们需要手动配置一下 android { //TODO 其他省略buildFe…

网络编程套接字(二)

目录 简单的TCP网络程序服务端创建套接字服务端绑定服务端监听服务端获取连接服务端处理请求单执行流服务器的弊端 多进程版TCP网络程序捕捉SIGCHLD信号让孙子进程提供服务多线程版的TCP网络程序客户端创建套接字客户端链接服务器客户端发起请求 线程池版的TCP网络程序 简单的T…

SpringBoot整合数据库版本管理工具Liquibase,赶紧整起来!

SpringBoot整合数据库版本管理工具Liquibase 背景一、什么是数据库版本管理工具&#xff1f;数据库版本管理工具主要特性什么是数据库版本管理工具Flyway和Liquibase对比及选型 二、Liquibase整合步骤1.引入pom依赖2.配置application.yml3.新建master.xml&#xff08;用于配置你…

使用pandas处理excel文件【Demo】

一、代码示例 import pandas as pd from pandas import Series,DataFrame from pandasql import sqldf import matplotlib.pyplotidInfos DataFrame(pd.read_excel(home_data.xlsx))print(idInfos.head(2))print(idInfos.dtypes)# print(idInfos[:][姓名]) # 自定义一个函数s…

QTreeView 常见节点操作

目录 1、节点遍历 2、设置当前选中项 3、树节点数据绑定 4、树节点自定义样式 5、数据检索 6、获取当前选中项 QTreeView作为项目最经常使用的空间&#xff0c;常用接口和操作必须熟悉熟悉在熟悉&#xff01;&#xff01;&#xff01; 1、节点遍历 void ParamSettingDl…

【python VS vba】(3) 在python直接调用vba脚本

目录 0 前言 1 VBA 内容 1.1 EXCEL这边VBA的内容 1.2 VBA的测试代码 2 python 调用 2.1 python 调用VBA的过程和结果 2.2 代码 0 前言 前面写了这么多&#xff0c;没想到&#xff0c;其实py是可以直接支持VBA的 python的模块import xlwings&#xff0c;可以让python直…

Leetcode1128. 等价多米诺骨牌对的数量

Every day a Leetcode 题目来源&#xff1a;1128. 等价多米诺骨牌对的数量 解法1&#xff1a;暴力 代码&#xff1a; class Solution { public:int numEquivDominoPairs(vector<vector<int>> &dominoes){int n dominoes.size(), count 0;for (int i 0;…

理解训练深度前馈神经网络的难度【PMLR 2010】

论文地址&#xff1a;Excellent-Paper-For-Daily-Reading/summarize at main 类别&#xff1a;综述 时间&#xff1a;2023/11/03 摘要 这篇论文比较久了&#xff0c;但仍能从里面获得一些收获&#xff0c;论文主要是讨论并研究了不同的非线性激活函数的影响&#xff0c;sig…

Java与Redis的集成以及Redis中的项目应用

一、Java连接Redis Redis与MySQL都是数据库&#xff0c;java操作redis其实跟操作mysql的过程是一样的。 1.1 导入依赖 打开IDEA&#xff0c;进入Java项目&#xff0c;导入pom依赖&#xff0c;代码如下&#xff1a; <dependency><groupId>redis.clients</gro…

threejs 2. 辅助对象

ArrowHelper 箭头 const arrowHelper new THREE.ArrowHelper(new THREE.Vector3( 1, 2, 0 ).normalize(), // 方向向量必须是单位向量,默认值为(0,0,1)new THREE.Vector3( 0, 0, 0 ), // 起点,默认值为(0,0,0)1, // 长度,默认值为10xffff00, // 颜色,默认值为0xffff00undefine…

JavaSE基础 --- 类与对象

1.类与对象的定义 类是一种抽象的数据类型&#xff0c;它描述了一类对象的行为和状态。例如&#xff0c;我们可以定义一个名为“Dog”的类&#xff0c;它描述了狗这类动物的一般特性&#xff0c;如颜色、品种等状态&#xff0c;以及跑、叫等行为。 对象则是类的实例&#xff0c…

【金TECH频道】企业架构转型组合拳来袭,助力金融机构一臂之力

当前&#xff0c;数字化转型已经成为时代共性课题 在政策和技术的双重指引下 金融机构逐渐走向差异化竞争的格局 面对转型阵痛 以契合、明晰的战略规划及 企业架构调整来辅助业务变革 成为助力企业数字化转型的有效路径 金融机构也纷纷开始探索 企业架构转型的新思路、…

SQL Server2000mdf升级SQL Server2005数据库还原

SQL Server2000数据库还原sqlserver 2000mdf升级 sqlserver 2008数据库还原SQL Server2005数据库脚本 sqlserver数据库低版本升级成高版本 sqlserver数据库版本升级 数据库版本还原 如果本机安装了sqlserver2012或者sqlserver2019等高版本 怎么样才能运行sqlserver2000的数据库…

2003-2022年地级市-财政收支明细数据(企业、个人所得税、科学、教育、医疗等)

2003-2022年地级市-财政收支明细数据&#xff08;企业、个人所得税、科学、教育、医疗等&#xff09; 1、时间&#xff1a;2003-2022年 2、指标&#xff1a;行政区划代码、年份、地区、一般公共预算收入、一般公共预算-税收收入、一般公共预算-税收收入-增值税收入、一般公共…

软件测试面试题及答案2024

1、你们的缺陷等级如何划分的&#xff1f;☆☆☆☆☆ 我们的缺陷一般分为四个等级&#xff0c;致命级&#xff0c;严重级&#xff0c;一般级和轻微级。致命级指能够导致软件程序无法使用的缺陷&#xff0c;比如宕机&#xff0c;崩溃&#xff0c;手机APP的闪退&#xff0c;数据…

【AUTOSAR CANTP】深入理解CAN传输层:N-SDU数据接收与缓冲处理

1. 前言 CanTp是PDU路由器和CAN接口模块之间的那个模块。它的主要作用就是对超过8字节或者CAN FD情况下超过64字节的CAN I-PDU进行分段和重组啦。PDU路由器会把AUTOSAR COM和DCM I-PDU放到不同的通信协议上去,具体是用哪个网络系统类型(比如CAN、LIN和FlexRay)来路由,就看…