[Linux]进程程序替换

[Linux]进程程序替换

文章目录

  • [Linux]进程程序替换
    • 进程程序替换的意义
    • 见一见进程程序替换
    • 进程程序替换的原理
    • 进程程序替换中的写时拷贝
    • 介绍进程程序替换接口

进程程序替换的意义

Linux系统下使用fork系统函数创建子进程后,子进程只能执行继承的部分父进程代码,如果要想让子进程执行一份单独的代码就要进行进程程序替换。

见一见进程程序替换

进程程序替换的头文件和函数如下:

image-20230831104800830

编写如下代码来见一见进程程序替换:

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

int main()
{
  printf("进程程序替换前\n");
  printf("进程程序替换前\n");
  printf("进程程序替换前\n");
  execl("/bin/ls", "ls", "-a", "-l", NULL);
  printf("进程程序替换后\n");
  printf("进程程序替换后\n");
  printf("进程程序替换后\n");
  return 0;
}

编译代码并运行查看结果:

image-20230831104439925

可以看出,进程程序替换后,进程不再执行原有的代码而是转而执行替换后的代码。进程替换函数后的代码不再被执行,因此可以看出程序替换是整体替换,替换后原有的代码和数据都不存在了。

进程程序替换的原理

进程程序替换是在不修改进程pcb中的id的情况下,将磁盘中的可执行程序的代码和数据传送到内存中,替换进程原有的代码和数据,并且修改页表映射,完成进程程序的替换,示意图如下:

image-20230831104507648

  • 从进程的角度看:代码和数据被操作系统替换了。
  • 从程序的角度看:自身的代码和数据被加载到了内存中。

程序加载的原理: 在Linux操作系统下,启动的任何一个进程都是shell进程的子进程,启动进程的就是先让shell进程创建一个子进程的pcb,然后用我们编写好的进程的代码和数据替换这个shell进程的子进程的,从而完成进程的加载启动。

进程程序替换中的写时拷贝

为了体会进程程序替换中的写时拷贝,编写如下代码:

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

int main()
{
  pid_t id = fork();
  if (id == 0)
  {
    //子进程
    printf("我是子进程,我的pid:%d\n", getpid());
    execl("/bin/ls", "ls", "-a", "-l", NULL);
  }
  waitpid(id, NULL, 0);
  printf("我是父进程,我的pid:%d\n", getpid());
  return 0;
}

编译代码运行并且查看结果:

image-20230831104608439

可以看出进程程序替换是不影响父进程的代码和数据的,因为在替换子进程的代码和数据时,发生了写时拷贝,另外可以看出实际上代码区的数据是可以修改的。

介绍进程程序替换接口

返回值

进程程序替换函数只有在执行失败时才会有返回值,如果进程程序替换失败了会返回-1,并且设置错误码。由于进程程序替换函数的替换成功后,该函数内部的返回代码也被替换了,因此一旦执行成功是不会有返回值的。一旦替换失败了,由于进程程序替换不会修改pcb,因此也不影响父进程接收子进程的退出码。

编写如下代码进行测试:

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

int main()
{
  pid_t id = fork();
  if (id == 0)
  {
    //子进程
    int n = execl("/bin/lsss", "lsss", "-a", "-l", NULL);
    printf("我是子进程,我的id:%d, 程序替换失败,n: %d\n", getpid(), n);
    exit(1);
  }
  int status = 0;
  waitpid(id, &status, 0);
  printf("我是父进程,子进程退出码:%d\n", WEXITSTATUS(status));
  return 0;
}

编译代码运行并查看结果:

image-20230831104523903

由于我们传入的替换程序是错误的,进程程序替换失败了,接收到了返回值-1,父进程也正常接收到了退出码-1。

再编写如下代码进行测试:

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

int main()
{
  pid_t id = fork();
  if (id == 0)
  {
    //子进程
    int n = execl("/bin/ls", "ls", "hello.txt", NULL);//该文件不存在
    printf("我是子进程,我的id:%d, 程序替换失败,n: %d\n", getpid(), n);
    exit(1);
  }
  int status = 0;
  waitpid(id, &status, 0);
  printf("我是父进程,子进程退出码:%d\n", WEXITSTATUS(status));
  return 0;
}

编译代码运行并查看结果:

image-20230831102532182

可以看出即使程序替换成功了,也不影响子进程退出码的接收。

execl函数

//execl函数声明
int execl(const char *path, const char *arg, ...);
  • path参数 – 要替换的程序的路径
  • arg参数 – 可变参数,接收替换程序的命令行参数,以NULL结尾

编写如下代码进行测试:

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

int main()
{
  printf("进程程序替换前\n");
  execl("/bin/ls", "ls", "-a", "-l", NULL);
  printf("进程程序替换失败\n");
  return 0;
}

编译代码运行并查看结果:

image-20230831102536883

execv函数

//execv函数声明
int execv(const char *path, char *const argv[]);
  • path参数 – 要替换的程序的路径
  • argv参数 – 接收要替换程序的命令行参数,以NULL结尾

编写如下代码进行测试:

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

int main()
{
  printf("进程程序替换前\n");
  //execl("/bin/ls", "ls", "-a", "-l", NULL);
  char * const argv[] = {
    "ls",
    "-a",
    "-l",
    "-n",
    NULL
  };
  execv("/bin/ls", argv);
  printf("进程程序替换失败\n");
  return 0;
}

编译代码运行并查看结果:

image-20230831104539226

execlp函数

//execlp函数声明
int execlp(const char *file, const char *arg, ...);
  • file参数 – 要替换的程序名,会自动去环境变量的路径中查找该程序
  • arg参数 – 可变参数,接收替换程序的命令行参数,以NULL结尾

编写如下代码进行测试:

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

int main()
{
  printf("进程程序替换前\n");
  execlp("ls", "ls", "-l", "-n", NULL);
  printf("进程程序替换失败\n");
  return 0;
}

编译代码运行并查看结果:

image-20230831104738842

execvp函数

//execvp函数声明
int execvp(const char *file, char *const argv[]);
  • file参数 – 要替换的程序名,会自动去环境变量的路径中查找该程序
  • argv参数 – 接收要替换程序的命令行参数,以NULL结尾

编写如下代码进行测试:

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

int main()
{
  printf("进程程序替换前\n");
  char * const argv[] = {
    "ls",
    "-l",
    "-n",
    NULL
  };
  execvp("ls", argv);
  printf("进程程序替换失败\n");
  return 0;
}

编译代码运行并查看结果:

image-20230831093047903

execle函数

//execle函数声明
int execle(const char *path, const char *arg, ..., char * const envp[]);
  • path参数 – 要替换的程序的路径
  • arg参数 – 可变参数,接收替换程序的命令行参数,以NULL结尾
  • envp参数 – 接收传给替换程序的环境变量,以NULL结尾,覆盖式传入,接收的进程中只会有传入的环境变量

编写如下文件目录结构:

image-20230831100637858

使用myproc程序和otherproc程序进行测试,其代码内容如下

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

int main()
{
  printf("进程程序替换前\n");
  char * const envp[] = {
    "MYENV=YOUCANSEEME",
    NULL 
  };
  execle("../otherproc/otherproc", "otherproc", NULL, envp);
  printf("进程程序替换失败\n");
  return 0;
}
//otherproc
#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
  cout << " MYENV: " << (getenv("MYENV")==NULL?"NULL":getenv("MYENV")) << endl;
  cout << " PATH: " << (getenv("PATH")==NULL?"NULL":getenv("PATH")) << endl;
  return 0;
}

使用otherproc程序替换myproc程序,并且只传入只含有一个环境变量的参数envp。

编译代码运行并查看结果:

image-20230831101123814

由于execle函数传入环境变量采用的是覆盖式传入,因此替换程序只有传入的一个环境变量。

补充:

  • 可以使用全局变量environ让替换程序获取父进程的全部环境变量。
  • shell进程创建子进程时也是调用这样的系统接口将环境变量传入。

execvpe函数

int execvpe(const char *file, char *const argv[], char *const envp[]);
  • file参数 – 要替换的程序名,会自动去环境变量的路径中查找该程序
  • argv参数 – 接收要替换程序的命令行参数,以NULL结尾
  • envp参数 – 接收传给替换程序的环境变量,以NULL结尾,覆盖式传入,接收的进程中只会有传入的环境变量

补充知识

程序替换函数的命名规律:

  • l后缀 – 以可变参数形式接收替换程序的命令行参数
  • v后缀 – 以指针数组的形式接收替换程序的命令行参数
  • p后缀 – 传入替换程序的程序名,会自动在环境变量的路径中查找
  • e后缀 – 以指针数组的形式接收替换程序的环境变量

以上所有的进程程序替换系统调用都是对execve系统调用函数的封装,不同的封装是为了更适合不同的应用场景。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

常用的css样式

1&#xff1a;flex布局 .flex-between {display: flex;justify-content: space-between; }.flex-evenly {display: flex;justify-content: space-evenly; }.flex-end {display: flex;justify-content: flex-end; }.flex {display: flex; }.flex-center {display: flex;justify…

JavaScript(函数,作用域和闭包)

目录 一&#xff0c;什么是函数1.1&#xff0c;常用系统函数1.2&#xff0c;函数声明 1.3&#xff0c;函数表达式二&#xff0c;预解析2.1&#xff0c;函数自调用 2.2&#xff0c;回调函数三&#xff0c;变量的作用域3.1&#xff0c;隐式全局变量 四&#xff0c;作用域与块级作…

关于sd卡根目录在哪里

你说的对,一切都会过去,哪怕是回忆。 sd卡根目录在哪里 1、手机要有SD卡才会存在SD卡根目录。 2、打开文件管理并在此页面下点击所有文件。 3、点击SD卡选项进入的页面就是SD卡根目录。 4、SD存储卡是一种基于半导体快闪记忆器的新一代记忆设备&#xff0c;由于它体积小、数…

[Pandas] pandas.melt

melt是溶解 / 分解的意思&#xff0c;即拆分数据 melt()函数可以将一些列的内容进行合并&#xff0c;把宽表整合成长表 语法格式 pandas.melt(frame, id_varsNone, value_varsNone, var_nameNone, value_namevalue)参数说明 frame&#xff1a;要处理的数据集 id_vars&#…

如何五分钟设计制作自己的蛋糕店小程序

在现如今的互联网时代&#xff0c;小程序已成为企业推广和销售的重要利器。对于蛋糕店来说&#xff0c;搭建一个小程序可以为其带来更多的品牌曝光和销售渠道。下面&#xff0c;我们将以乔拓云平台为例&#xff0c;来教你如何从零开始搭建自己的蛋糕店小程序。 首先&#xff0c…

docker 安装 Nginx

1、下载 docker pull nginx:latest 2、本地创建管理目录 mkdir -p /var/docker/nginx/conf mkdir -p /var/docker/nginx/log mkdir -p /var/docker/nginx/html 3、将容器中的相应文件复制到管理目录中 /usr/docker/nginx docker run --name nginx -p 80:80 -d nginxdocke…

Golang数据结构和算法

Golang数据结构和算法 数据的逻辑结构和物理结构常见数据结构及其特点算法的时间复杂度和空间复杂度Golang冒泡排序Golang选择排序Golang插入排序Golang快速排序Golang归并排序Golang二分查找Golang sort包Golang链表Golang container/list标准库Golang栈stackGolang二叉搜索树…

Spring Security存在认证绕过漏洞 CVE-2021-22096

文章目录 0.前言1.参考文档2.基础介绍漏洞影响范围&#xff1a;官方说明&#xff1a;修复版本&#xff1a;漏洞利用步骤&#xff1a;修复方式&#xff1a; 3.解决方案 0.前言 背景&#xff1a;项目被扫到Spring Boot 的漏洞&#xff0c;严格的说应该是Spring Security 组件的漏…

小程序隐私保护授权处理方式之弹窗组件

欢迎点击关注-前端面试进阶指南&#xff1a;前端登顶之巅-最全面的前端知识点梳理总结 *分享一个使用比较久的&#x1fa9c; 小程序隐私保护授权弹窗组件 调用wx.getUserProfile进行授权时&#xff0c;返回错误信息&#xff1a;{errMsg: “getUserProfile:fail api scope is…

宿舍固定资产怎么管理

宿舍固定资产的管理需要做到以下几点&#xff1a; 固定资产购置&#xff1a;宿舍的固定资产包括设备、家具、厨房用品等&#xff0c;购置时需要注意质量和价格&#xff0c;并进行登记。 固定资产登记&#xff1a;将宿舍的固定资产名称、型号、规格、数量、单价、金额、…

详细说明OSPF常见的LSA

目录 1类LSA &#xff08;Router LSA&#xff09;介绍 总结&#xff1a;1类LSA 2类LSA &#xff08;Network LSA&#xff09;介绍 总结&#xff1a;2类LSA 3类LSA &#xff08;Summary LSA&#xff09;介绍 总结&#xff1a;3类LSA 5类LSA &#xff08;ase LSA&…

layUI 中 穿梭框无法获取值的细节问题

初始化的时候一定要指定id&#xff0c;不然就会出现无法调用 获得右侧数据和实例重载的方法

高忆管理:A股上市券商“中考”成绩放榜,最大黑马是它

A股上市券商2023年半年报发表8月30日晚正式收官。全体上看&#xff0c;43家券商中有10家营收超百亿元&#xff0c;多达30家完成了营收及净利润的双增。头部券商中&#xff0c;我国银河近年来运营成绩排名稳步提高&#xff1b;区域性券商中&#xff0c;天风证券成最大黑马&#…

经典文献阅读之--FastSAM(快速分割一切)

0. 简介 MetaAI提出的能够“分割一切”的视觉基础大模型SAM提供了很好的分割效果&#xff0c;为探索视觉大模型提供了一个新的方向。虽然SAM的效果很好&#xff0c;但由于SAM的backbone使用了ViT&#xff0c;导致推理时显存的占用较多&#xff0c;推理速度偏慢&#xff0c;对硬…

多图详解VSCode搭建Java开发环境

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

LeetCode--HOT100题(46)

目录 题目描述&#xff1a;114. 二叉树展开为链表&#xff08;中等&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;114. 二叉树展开为链表&#xff08;中等&#xff09; 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链…

qt day 1

this->setWindowIcon(QIcon("D:\\zhuomian\\wodepeizhenshi.png"));//設置窗口的iconthis->setWindowTitle("鵬哥快聊");//更改名字this->setFixedSize(500,400);//設置尺寸QLabel *qlnew QLabel(this);//創建一個標簽ql->resize(QSize(500,20…

Stable Diffusion WebUI 整合包

现在网络上出现的各种整合包只是整合了运行 Stable Diffusion WebUI&#xff08;以下简称为 SD-WebUI&#xff09;必需的 Python 和 Git 环境&#xff0c;并且预置好模型&#xff0c;有些整合包还添加了一些常用的插件&#xff0c;其实际与手动进行本地部署并没有区别。 不过&a…

[linux实战] 华为云耀云服务器L实例 Java、node环境配置

系列文章目录 第一章 [linux实战] 华为云耀云服务器L实例 Java、node环境配置 文章目录 系列文章目录前言一、任务拆解二、修改密码三、配置安全规则四、远程登录并更新apt五、安装、配置JDK环境5.1、安装openjdk,选择8版本5.2、检查jdk配置 六、安装、配置git6.1、安装git6.2…

基于Java+SpringBoot+Mybaties-plus+Vue+ElementUI 高校汉服租赁网站的 设计与实现

一.项目介绍 高校汉服租赁网站分为普通用户以及管理员两类 普通用户&#xff1a; 注册、登录系统、查看汉服首页发帖公告信息、 交流论坛&#xff08;发帖、查看帖子、评论&#xff09;、 公告咨询&#xff08;查看公告以及评论&#xff09;、 汉服信息&#xff08;查…