Linux操作系统学习(进程替换)

news2025/4/27 4:43:04

文章目录

  • 进程替换
    • 进程替换是什么?
    • 替换的方法
    • 进程替换
  • 简易shell模拟

进程替换

进程替换是什么?

如下图所示:

进程替换就是,把进程B的代码和数据,替换正在执行的进程A的代码和数据在内存中的位置(若代码数据过多可能会改变页表),但进程A的整体部分不发生任何改变(task_struct、A进程地址空间等等)

其实就是用A进程的壳子执行B进程程序,不改变A进程的任何东西,只改变页表物理地址部分和内存中的数据和代码,不创建任何新的进程,并且子进程也不会退出。

替换的方法

一般用到以下六种函数

#include <unistd.h>
int exec l(const char *path, const char *arg, …);
int exec lp(const char *file, const char *arg, …);
int exec le(const char *path, const char *arg, …,char *const envp[]);
int exec v(const char *path, char *const argv[]);
int exec vp(const char *file, char *const argv[]);
int exec ve(const char *path, char *const argv[], char *const envp[]);

命名后缀:

  • l(list) : 表示参数采用列表

  • v(vector) : 参数用数组

  • p(path) : 有p自动搜索环境变量PATH

  • e(env) : 表示自己维护环境变量

    程序运行时的环境变量信息(函数不会给你自动继承父进程的环境变量,需要手动设置)

返回值:

若替换失败则返回-1,但其实可以不用检查返回值因为:

  • 调用成功一定执行替换的程序

  • 调用失败一定执行原本的程序

  • int execl(const char *path, const char *arg, …);

void test1()    
{    
pid_t id = fork();    
if(id == 0)    
{    
printf("你好\n");    

  /*********************************************开始替换******************************************/
    execl("/usr/bin/ls","ls","-a","-l","-i",NULL);   
  //你要执行谁,想怎么执行(在命令行怎么执行就怎么执行),可变参数列表以NULL结尾
  //或者想要执行自己的程序 execl("./当前路径或者 /.../...绝对路径","可执行程序名",NULL);
  /*********************************************替换完成/失败******************************************/
  
  printf("hello\n");    
  }    
  sleep(1);    
printf("child exchange succeed\n");     
}    

  • int execlp(const char *file, const char *arg, …);
void test2()
{
  pid_t id = fork();
  if(id == 0)
  {
    char* argv[] = {"ls","-a","-i","-l",NULL};//就是把可变参数列表以数组的形式传给execv
    printf("exchange test2--->:\n");
/*********************************************开始替换******************************************/
    execv("/usr/bin/ls",argv);
/*********************************************替换完成/失败******************************************/
    printf("exchange fail\n");
  }
  sleep(1);
  printf("exchange succeed\n");
}

  • int execle(const char *path, const char *arg, …,char *const envp[]);
void test3()
{
  pid_t id = fork();
  if(id == 0)
  {
    printf("exchange test3---->\n");
/*********************************************开始替换******************************************/
    execlp("ls","ls","-l","-a","-i",NULL);
    // 第一个你要执行的是谁但不用带路径,path会根据这个程序名去自动搜索它在什么位置,第二个是要怎么执行
/*********************************************替换完成/失败******************************************/
    printf("exchange fail\n");
  }
  sleep(1);
  printf("exchange succeed\n");

}

  • int execvp(const char *file, char *const argv[]);
void test4()
{
  pid_t id = fork();
  if(id == 0)
  {
    printf("exchange test4---->\n");
    char* argv[] = {"ls","-a","-l","-i",NULL};
/*********************************************开始替换******************************************/
    execvp("ls",argv); 
    //第一个参数告诉path要执行的程序他会自动去找路径,第二个参数从可变参数列表变为自定义数组
/*********************************************替换完成/失败******************************************/
    printf("exchange fail\n");
  }
  sleep(1);
  printf("exchange succeed\n");
}

  • int execle(const char *path, const char *arg, …,char *const envp[]);
void test5()
{
  pid_t id = fork();
  if(id == 0)
  {
    printf("exchange test5---->\n");
/*********************************************开始替换******************************************/
    char* env[] = {"my_env=hello",NULL};
    execle("./print","print",NULL,env);
    //最后一个参数env指定了新程序的环境列表。参数env对应于新程序的environ数组
    //传递自己的环境变量给print
/*********************************************替换完成/失败******************************************/
    printf("exchange fail\n");
  }
  sleep(1);
  printf("exchange succeed\n");

}

int main()
{
  extern char** environ;
  for(int i = 0;environ[i];i++)
  {
    if(environ[i] == "PATH")
      continue;//path显示的太多,这里屏蔽掉
    printf("%s\n",environ[i]);

  }

  return 0;
}

  • int execve(const char *path, char *const argv[], char *const envp[]);
void test6()
{
  pid_t id = fork();
  if(id == 0)
  {
    printf("exchange test5---->\n");
/*********************************************开始替换******************************************/
    char* argv[] = {"print",NULL};
    char* env[] = {"my_env=hello",NULL};
    execve("./print",argv,env);
/*********************************************替换完成/失败******************************************/
    printf("exchange fail\n");
  }
  sleep(1);
  printf("exchange succeed\n");

}

int main()
{
  extern char** environ;
  for(int i = 0;environ[i];i++)
  {
    if(environ[i] == "PATH")
      continue;
    printf("%s\n",environ[i]);

  }

  return 0;
}

可以看出所有的函数都是在execve基础上封装的


进程替换

  • 子进程需要替父进程执行一些任务就需要进程替换

  • 进程替换只替换子进程在内存中的代码和数据,以及页表物理地址部分

  • 进程替换不会创建新进程,不会退出子进程

  • 虽然父子代码是共享的,但是进程替换会更改内存的代码和数据,所以要发生写实拷贝

  • fork创建子进程后,在代码中exec…只会替换子进程,因为进程具有独立性

程序替换的本质是把程序的代码数据加载到指定进程的上下文中


简易shell模拟

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

void myshell()
{
  char command[128];
  char* argv[64];
  while(1)
  {
    command[0] = 0;
    printf("[awd@VM-16-4-centos myshell]----->");	//打印前缀	
    fflush(stdout);									//刷新缓冲区
   
    fgets(command,128,stdin);						//输入命令
    command[strlen(command) - 1] = 0;				//先当作整个字符串存入command,-1是除去\n
    //fflush(stdout);									
    //printf("%s\n",command);验证


    const  char* set = " ";							//设置分隔符
    argv[0] = strtok(command,set);					//把字符串拆解成指令
    int i = 1;								
    while( argv[i] = strtok(NULL,set) )				//类似strcpy,赋值到NULL退出
      i++;
    /*for(int j = 0;j < i;j++)
      printf("%s\n",argv[j]);验证*/


    if(strcmp(argv[0],"cd") == 0)					//在子进程cd影响的只是子进程,所以要再父进程处理
    {
      if(argv[1])
        chdir(argv[1]);
      continue;
    }
       
       
					
     if(fork() == 0)								 //创建子进程
     {
       execvp(argv[0],argv);						 //替父进程执行这些指令
       exit(1);								     	 //若执行到这说明替换失败,设置退出码为1
     }

							
        
    waitpid(-1,NULL,0);								//等待任意一个子进程结束
    int status = 0;
    if(strcmp(argv[0],"echo") == 0 && strcmp(argv[1],"$?") == 0)	//打印退出码和终止信号
      printf("exit code:%d ,exit signal:%d \n",WEXITSTATUS(status),WTERMSIG(status));


  }

}

int main()
{
  myshell();
  return 0;
}

通过这个简易shell来把之前学到的总结一下

  • 一般让子进程替父进程执行一些第三方命令,那么就需要用到 进程替换和fork
  • 子进程每次执行结束需要进程等待,为了结束他的僵尸进程并获取它的退出信息(退出码、终止信号
  • 每次进程退出后又会重新创建子进程,所以echo $? 查看的是最近一次执行的退出码
  • 证明了每一次命令行执行的指令都是一次进程,是基于bash为父进程创建的子进程

上面这个简陋shell综合了 :fork、进程替换函数、进程等待函数、进程退出函数、退出码/终止信号,加深了这些接口的理解

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

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

相关文章

etcd集群通过 Leader 写入数据,为什么K8s HA集群中讲每个 kube-apiserver 只和本机的 ETCD 通信

写在前面 对这个我不太明白&#xff0c;所有在 stackOverflow 的请教了大佬这里分享给小伙伴理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&#xff0c;永不停息。所有其它的路都是不完整…

spark sql(二)sql解析流程扩展

1、前言 通过前面的文章我们了解到&#xff0c;spark sql通过catalyst框架解析sql&#xff0c;而在将sql语句转变为可执行的任务过程中会将大的sql解析流程划分为未解析的逻辑计划、解析后的逻辑计划、优化后的逻辑计划、物理计划、可执行物理计划等阶段。大概的解析流程如下所…

Handler与线程

简介 Handler提供的种异步消息处理机制是&#xff1a;当它发出一个消息进入消息队列后&#xff0c;发送消息的函数立刻返回&#xff0c;接着主线程会逐个地从消息队列中把消息取出&#xff0c;然后对消息进行处理。明显&#xff0c;Handler发送消息和接收消息是异步进行的&…

三八送什么数码产品好?适合送礼的数码产品

数码产品是我们生活中比较常见到的物品&#xff0c;相比较于一般礼物的观赏性&#xff0c;它的实用性更强一些&#xff0c;所以如果你不知道送什么礼物给别人的话&#xff0c;数码产品也是不错的选择。 一、南卡小音舱蓝牙耳机 这个时代的女性&#xff0c;变得越来越自信了&am…

ChatGPT解答:根据使用者输入的字符串,自动判断规则,并给出各种正则表达式,用Python实现

ChatGPT解答&#xff1a; 根据使用者输入的字符串&#xff0c;自动判断规则&#xff0c;并给出各种正则表达式&#xff0c;用Python实现 根据输入的字符串&#xff0c;自动给出正则表达式 根据使用者输入的字符串&#xff0c;自动判断规则&#xff0c;并给出各种正则表达式&am…

JVM系统优化实践(7):垃圾回收器与垃圾回收算法

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;上回说到了年轻代、老年代与数据计算的一个案例。接下来就先讲一讲年轻代和老年代的两个垃圾回收器&#xff1a;ParNew和CMS。和Serial垃圾回收器一样&#xff0c…

实战:yaml方式安装ingress-nginx-2023.3.2(测试成功)

实战&#xff1a;yaml方式安装ingress-nginx-2023.3.2(测试成功) 目录 文章目录实战&#xff1a;yaml方式安装ingress-nginx-2023.3.2(测试成功)目录实验环境实验软件1、安装过程2、第一个示例关于我最后最后实验环境 实验环境&#xff1a; 1、win10,vmwrokstation虚机&#x…

AI_News周刊:第四期

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 News 1.对抗“唤醒人工智能”马斯克招募团队开发 OpenAI 竞争对手 据两位直接了解这项工作的人士和另一位了解情况的人士透露&#xff0c;埃隆马斯克最近几周与人工智能研究人员接洽&#xff0c;商讨成…

详细分析什么是进程?如何理解进程状态?

什么是进程&#xff1f; 比较官方一点的回答是&#xff1a;当一个程序加载到内存的时候&#xff0c;就是一个进程。 但是这是不准确的回答&#xff0c;进程是怎么在内存中形成的&#xff0c;以及内存是如何管理进程的&#xff0c;是通过什么描述进程的&#xff1f;下面我们将…

Neo4j数据库部署配置

这里写目录标题一、neo4j图形数据库安装与部署1.1配置JDK运行环境&#xff08;注意jdk与neo4j版本对应&#xff09;1.2部署Neo4j&#xff08;注意jdk与neo4j版本对应&#xff09;二、数据库基本操作演示一、neo4j图形数据库安装与部署 1.1配置JDK运行环境&#xff08;注意jdk与…

centos安装rocketmq

centos安装rocketmq1 下载rocketmq二进制包2 解压二进制包3 修改broker.conf4 修改runbroker.sh和runserver.sh的JVM参数5 启动NameServer和Broker6 安装rockermq dashboard(可视化控制台)1 下载rocketmq二进制包 点击rocketmq二进制包下载地址&#xff0c;下载完成之后通过ft…

javaEE 初阶 — 数据链路层中的以太网数据帧

文章目录以太网帧格式1. MAC 地址2. MAC 地址是如何与 IP 地址相互配合的3. 以太网帧格式中的类型MTU&#xff08;了解&#xff09;以太网帧格式 数据链路层主要考虑的是相邻的两个结点之间的传输。 这里最知名的协议就是 以太网。 一个以太网数据帧有三个部分组成。帧头载荷…

【GlobalMapper精品教程】055:GM坐标转换器的巧妙使用

GM软件提供了一个简单实用的坐标转换工具,可以实现地理坐标和投影坐标之间的高斯正反算及多种转换计算。 文章目录 一、坐标转换器认识二、坐标转换案例1. 地理坐标←→地理坐标2. 地理坐标←→投影坐标三、在输出坐标上创建新的点四、其他转换工具的使用一、坐标转换器认识 …

653600-56-7,Ac4GaINAz,N-叠氮四酰化半乳糖用于PROTAC合成

基础产品数据&#xff1a;CAS号&#xff1a;653600-56-7中文名&#xff1a;N-叠氮四酰化半乳糖&#xff0c;叠氮修饰半乳糖英文名&#xff1a; Ac4GaINAzAc4GaINAz结构式&#xff08;Structural&#xff09;&#xff1a;详细产品数据&#xff1a;分子式&#xff1a;C16H22N4O10…

python学习——【第二弹】

前言 上一篇文章 python学习——【第一弹】给大家介绍了python中的基本数据类型等&#xff0c;这篇文章接着学习python中的运算符的相关内容。 运算符 python中的运算符主要有&#xff1a;算术运算符&#xff0c;赋值运算符&#xff0c;比较运算符&#xff0c;布尔运算符以及…

NPP夜间灯光遥感数据读取与可视化

1、Google Earth EngineGoogle Earth Engine是Google推出的行星尺度的遥感云计算平台&#xff0c;提供了大量遥感数据的集成与运算工具。同时也包括DMSP和NPP夜间灯光遥感数据&#xff08;月尺度和年尺度&#xff09;。这里给出样例的可视化代码。var dataset ee.ImageCollect…

1.2 CSS标签选择器,类选择器

CSS选择器&#xff1a; 根据不同的需求选出不同的标签&#xff0c;进行美化装饰 1. 标签选择器 标签选择器(元素选择器)&#xff1a;用 HTML标签名作为选择器&#xff0c;按标签名称进行分类&#xff0c;为页面某一类标签指定统一的CSS样式 作用: 可以把某一类标签全部选中&…

UWB通道选择、信号阻挡和反射对UWB定位范围和定位精度的影响

&#xff08;一&#xff09;介绍检查NLOS操作时需要考虑三个方面&#xff1a;&#xff08;1&#xff09;由于整体信号衰减&#xff0c;通信范围减小。&#xff08;2&#xff09;由于直接路径信号的衰减&#xff0c;导致直接路径检测范围的减小。&#xff08;3&#xff09;由于阻…

记录--手摸手带你撸一个拖拽效果

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前言 最近看见一个拖拽效果的视频(抖音&#xff1a;艾恩小灰灰)&#xff0c;看好多人评论说跟着敲也没效果&#xff0c;还有就是作者也不回复大家提出的一些疑问&#xff0c;本着知其然必要知其所以然…

栈帧之局部变量表(Local Variables)解读

局部变量表也被称之为局部变量数组或本地变量表 定义为一个数字数组&#xff0c;主要用于存储方法参数和定义在方法体内的局部变量&#xff0c;这些数据类型包括各类基本数据类型、对象引用&#xff08;reference&#xff09;&#xff0c;以及returnAddress类型。由于局部变量表…