[Linux]进程程序替换

news2024/11/25 11:54:24

[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系统调用函数的封装,不同的封装是为了更适合不同的应用场景。

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

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

相关文章

OB Cloud:如何为用户提供可持续的降本增效?

易鸿伟 OceanBase资深研发总监 2014年加入蚂蚁集团&#xff0c;深度参与了蚂蚁全站单元化架构、云架构、大促、网商银行的等几乎所有重点架构的升级与建设&#xff0c;主导集团内第四代微服务框架的重构&#xff0c;第五代数据访问层的重构&#xff0c;目前主要负责云数据库相…

解释基本的3D理论

推荐&#xff1a;使用 NSDT场景编辑器 快速搭建3D应用场景 坐标系 3D 本质上是关于 3D 空间中形状的表示&#xff0c;并使用坐标系来计算它们的位置。 WebGL 使用右侧坐标系 — 轴指向右侧&#xff0c;轴指向上方&#xff0c;轴指向屏幕外&#xff0c;如上图所示。xyz 对象 …

数据可视化与数字孪生:理解两者的区别

在数字化时代&#xff0c;数据技术正在引领创新&#xff0c;其中数据可视化和数字孪生是两个备受关注的概念。尽管它们都涉及数据的应用&#xff0c;但在本质和应用方面存在显著区别。本文带大探讨数据可视化与数字孪生的差异。 概念 数据可视化&#xff1a; 数据可视化是将复…

宿舍固定资产怎么管理

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

淘宝API接口:提高电商运营效率与用户体验的利器(淘宝API接口使用指南)

淘宝API接口&#xff1a;提高电商运营效率与用户体验的利器 随着电商行业的快速发展&#xff0c;淘宝作为国内最大的电商平台之一&#xff0c;不断探索和创新&#xff0c;以满足不断变化的用户需求和商家需求。其中&#xff0c;淘宝API接口便是其创新的一个重要方面。本文将深…

ReactPy:使用 Python 构建动态前端应用程序

在 Web 开发领域,ReactJS 已成为主导者,为开发人员提供了用于创建动态和交互式用户界面的强大工具集。但是,如果您更喜欢 Python 的多功能性和简单性作为后端,并且希望在前端也利用它的功能,该怎么办?ReactPy 是一个 Python 库,它将熟悉的 ReactJS 语法和灵活性带入了 P…

通过类定义一个网络

import torch from torch import nnx torch.ones(2,10)class MLP(nn.Module):def __init__(self):super().__init__()self.out nn.Linear(10, 1)def forward(self,x):return self.out(x) 1. 代码解析 如何定义一个类&#xff1f;self 又是什么东西&#xff1f;类是如何继承基…

高尔夫APP外包开发主要功能

高尔夫小程序可以实现教练预约、场地预地、训练课程、积分系统、社交功能等&#xff0c;通过小程序方便用户&#xff0c;同时也提球场的管理能力。今天和大家分享一些主要功能和注意的问题&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包…

详细说明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&…

二肽-2——祛除眼部水肿和眼部黑眼圈

简介 眼袋形成的一个重要的原因是水肿, 诱因主要是淋巴循环减弱和毛细血管的通透性增加。 INCI 名称 二肽-2 多肽序列 VW CAS号 24587-37-9 机理 抑制血管紧张素转换酶&#xff0c;增强眼部淋巴循环&#xff0c;促进水分排出 二肽-2是一种二胜肽&#xff0c;带有二种标…

高忆管理:沪指弱势调整跌0.53%,地产板块走弱,光刻机概念拉升

31日早盘&#xff0c;A股两市弱势调整。截至午间收盘&#xff0c;沪指跌0.53%报3120.39点&#xff0c;深成指跌0.55%&#xff0c;创业板指跌0.54%&#xff0c;两市算计成交5291亿元。北向资金净流出36亿元。盘面上&#xff0c;半导体、中成药、黄金等板块走强&#xff0c;地产、…

生成式人工智能能否使数字孪生在能源和公用事业行业成为现实?

推荐&#xff1a;使用 NSDT场景编辑器 快速搭建3D应用场景 克服障碍&#xff0c;优化数字孪生优势 要实现数字孪生的优势&#xff0c;您需要数据和逻辑集成层以及基于角色的演示。如图 1 所示&#xff0c;在任何资产密集型行业&#xff08;如能源和公用事业&#xff09;中&…

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

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

iOS逆向进阶:iOS进程间通信方案深入探究与local socket介绍

在移动应用开发中&#xff0c;进程间通信&#xff08;Inter-Process Communication&#xff0c;IPC&#xff09;是一项至关重要的技术&#xff0c;用于不同应用之间的协作和数据共享。在iOS生态系统中&#xff0c;进程和线程是基本的概念&#xff0c;而进程间通信方案则为应用的…

行政固定资产应该怎么管理

行政需要管理的固定资产主要包括办公设备、交通工具、通讯设备、家具等。具体来说&#xff0c;行政需要管理的固定资产包括但不限于&#xff1a;电脑、打印机、传真机、复印机、投影仪、电话、传真机、传真纸、电话线、路由器、交换机、服务器、UPS电源、办公桌椅、沙发等。 行…

Java小项目【图书馆系统】

一、设计图书馆系统 Java是一个面向对象的语言&#xff0c;在编写代码的之前&#xff0c;我们要先确定有哪些对象 图书馆&#xff0c;首先有很多书&#xff0c;还有书架来放置这些书。然后是对书进行操作的人&#xff0c;比如普通用户和管理员。最后是对关于书的各种操作&#…

如何检测勒索软件攻击

什么是勒索软件 勒索软件又称勒索病毒&#xff0c;是一种特殊的恶意软件&#xff0c;又被归类为“阻断访问式攻击”&#xff08;denial-of-access attack&#xff09;&#xff0c;与其他病毒最大的不同在于攻击方法以及中毒方式。 攻击方法&#xff1a;攻击它采用技术手段限制…

软件系统第三方检测费标准

收费标准 软件系统第三方检测收费标准&#xff1a; 行业内对于第三方软件测试报告并没有一个明确的收费标准&#xff0c;不同地域之间的收费不同&#xff0c;各个检测单位的报价也略有差异。第三方检测报告的收费标准需要根据具体的测试需求而定&#xff0c;一般是按照项目大…

“算力+运力”扇动双翼,制造算力时代的蝴蝶效应

8月18日-20日&#xff0c;第二届中国算力大会在宁夏银川成功举办。 今年以来&#xff0c;随着大模型、AIGC等新技术的火爆&#xff0c;站在舞台中央的算力承载了无尽的期待&#xff0c;发展数字经济需要以算力基础设施为前提&#xff0c;社会各界已经形成了共识。 与此同时&…

一文速学-让神经网络不再神秘,一天速学神经网络基础(五)-最优化

前言 思索了很久到底要不要出深度学习内容&#xff0c;毕竟在数学建模专栏里边的机器学习内容还有一大半算法没有更新&#xff0c;很多坑都没有填满&#xff0c;而且现在深度学习的文章和学习课程都十分的多&#xff0c;我考虑了很久决定还是得出神经网络系列文章&#xff0c;…