【Linux】进程替换

ops/2024/11/25 13:59:27/

进程替换

    • 一、概念
    • 二、原理
    • 三、替换函数
      • 1. execl
      • 2. execlp
      • 3.execle
      • 4.execv
      • 5.execvp
      • 6.execvpe
    • 四、实现一个简易的shell

一、概念

当我们fork()生成子进程后,子进程的代码与数据可以来自其他可执行程序。把磁盘上其他程序的数据以覆盖的形式给子进程。这样子进程就可以执行全新的程序了,这种现象称为程序替换。

二、原理

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

三、替换函数

1. execl

①函数原型:

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

②参数解析:

  • path:为可执行程序的路径;
  • arg:如何执行这个可执行程序(执行该程序的指令);
  • … :指的是给这执行程序携带的参数,在参数末尾加NULL表示参数结束;
  • 返回值:函数调用失败返回-1,成功不返回

③示例:

替换成功:

#include<stdio.h>
#include<unistd.h>
int main()
{printf("before\n");execl("/usr/bin/ls","ls","-a","-l",NULL);printf("after\n");return 0;
}

在这里插入图片描述

替换失败:

#include<stdio.h>
#include<unistd.h>
int main()
{printf("before\n");execl("/usr/bin/la","ls","-a","-l",NULL);printf("after\n");return 0;
}

在这里插入图片描述

通过对比可以发现,替换成功以后,原程序后面的代码就不执行了,执行另一个程序 的代码,失败就接着执行原程序的代码。

2. execlp

①函数原型:

 int execlp(const char *file, const char *arg, ...);

②参数解析:

  • file:要替换的目标程序;
  • arg:如何执行这个程序,…为给这个程序传的参数;
  • 返回值:函数调用失败返回-1;

③示例:

比较于execl,execp默认在Linux环境变量PATH中查找可执行程序,所以传参可直接传可执行程序的名字,不用传绝对路径。

#include<stdio.h>
#include<unistd.h>
int main()
{printf("before\n");execlp("ls","ls","-a","-l",NULL);printf("after\n");return 0;
}

在这里插入图片描述

3.execle

①函数原型:

int execle(const char *path, const char *arg, ..., char * const envp[]);

②参数解析:

  • path:替换目标程序路径;
  • arg:如何执行这个程序,…为给这个程序传的参数;
  • envp数组:要导入的环境变量;
  • 返回值:失败返回-1;

③示例:

查看环境变量代码,用来验证后面的结果

#include<stdio.h>
#include<stdlib.h>
int main()
{printf("path:%s\n",getenv("PATH"));printf("aaa:%s\n",getenv("aaa"));return 0;
}

在这里插入图片描述

#include<stdio.h>
#include<unistd.h>
int main()
{char* envp[]={"aaa=hello",NULL};printf("before\n");execle("./a.out","./a.out",NULL,envp);printf("after\n");return 0;
}

在这里插入图片描述

注意:

  1. 导环境变量的数组最后以NULL结尾
  2. 导入环境变量后原系统环境变量的值被清空,这种导入环境变量的方式为覆盖式导入

4.execv

①函数原型:

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

②参数解析:

  • path:替换目标程序路径;
  • argv数组:保存的是参数列表,将如何执行 可执行程序 和可执行程序需要的参数保存到字符串数组中,最后以NULL结尾表示参数结束;
  • 返回值:进程替换失败返回-1

③示例:

#include<stdio.h>
#include<unistd.h>
int main()
{char* argv[]={"ls","-a","-l",NULL};printf("before\n");execv("/usr/bin/ls",argv);printf("after\n");return 0;
}

在这里插入图片描述

注:可以发现argv数组与main函数的命令行参数相同

5.execvp

①函数原型:

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

②参数解析:

  • file:要替换的目标程序;
  • argv数组:保存的是参数列表,将如何执行 可执行程序 和可执行程序需要的参数保存到字符串数组中,最后以NULL结尾表示参数结束;

③示例:略

6.execvpe

①函数原型:

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

②参数解析:

  • file:要替换的目标程序;
  • argv数组:保存的是参数列表,将如何执行 可执行程序 和可执行程序需要的参数保存到字符串数组中,最后以NULL结尾表示参数结束;
  • envp数组:要导入的环境变量;

③示例:略

小结:

替换函数前面的exec不变

  • l:参数采用列表
  • v:参数采用数组
  • p:不需要输入路径,在环境变量自动搜索
  • e:要导入自己的环境变量

所以execvp表示不需要输入路径,参数用数组传
execve表示需要输入路径,参数用数组传,自己维护环境变量

四、实现一个简易的shell

#include<stdio.h>
#include<unistd.h>
#include<assert.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
#define MAX 1024
#define ARGC 64
#define SEP " "//将输入的字符串切割并保存到argv中
int split(char*commandstr,char*argv[])
{assert(commandstr);assert(argv);argv[0]=strtok(commandstr,SEP);if(argv[0]==NULL)return -1; //若为NULL,则重新输入int i=1;while(argv[i++]=strtok(NULL,SEP));return 0;
}
int main()
{ while(1){char commandstr[MAX]={NULL}; //用于保存用户输入的指令char*argv[ARGC]={NULL};printf("[lx@hecs-%d myshell]$ ",getpid());fflush(stdout);char*s=fgets(commandstr,sizeof(commandstr),stdin); //获取指令assert(s);(void)s;commandstr[strlen(commandstr)-1]='\0'; //去掉键盘输入的'\n'int n=split(commandstr,argv);  //切割输入的指令字符串if(n!=0)continue;pid_t id=fork();if(id==0){execvp(argv[0],argv); //程序替换exit(0);}int status=0;waitpid(id,&status,0); //等待子进程}return 0;
}

在这里插入图片描述

可以看到简易版的myshell就实现好了。

myshellplus

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <assert.h>#define MAX 1024
#define ARGC 64
#define SEP " "int split(char* commandstr,char* argv[])
{assert(commandstr);assert(argv);argv[0] = strtok(commandstr,SEP);if(argv[0] == NULL) return -1;int i = 1;while((argv[i++] = strtok(NULL,SEP)));return 0;
}void showEnv()
{extern char** environ;for(int i = 0; environ[i]; i++) printf("%d:%s\n",i,environ[i]);
}int main()
{extern int putenv(char* string);char myenv[32][256];int env_index = 0;int exitCode = 0;while(1){char commandstr[MAX] = {0};char* argv[ARGC] = {NULL};printf("[hxy@mychaimachine]$ ");fflush(stdout);char* s = fgets(commandstr,sizeof(commandstr),stdin);assert(s);(void)s;commandstr[strlen(commandstr) - 1] = '\0'; // 去掉键盘输入的\nint n = split(commandstr,argv); // 切割字符串if(n != 0) continue;if(strcmp(argv[0],"cd") == 0){if(argv[1] != NULL) chdir(argv[1]);continue;}else if(strcmp(argv[0],"export") == 0){if(argv[1] != NULL){strcpy(myenv[env_index],argv[1]); // 用户自己定义的环境变量,需要bash自己来维护putenv(myenv[env_index++]);}continue;}else if(strcmp(argv[0],"env") == 0){showEnv(); // env查看环境变量时,其实看的是父进程bash的变量continue;}else if(strcmp(argv[0],"echo") == 0){const char* target_env = NULL;if(argv[1][0] == '$'){if(argv[1][1] == '?'){printf("%d\n",exitCode);continue;} else target_env = getenv(argv[1] + 1);if(target_env != NULL) printf("%s = %s\n",argv[1] + 1,target_env);}continue;}// ls设置颜色选项if(strcmp(argv[0],"ls") == 0){int pos = 0;while(argv[pos] != NULL){pos++;}argv[pos++] = (char*)"--color=auto";argv[pos] = NULL;}pid_t id = fork();if(id == 0){// 子进程execvp(argv[0],argv);exit(1);}int status = 0;pid_t ret = waitpid(id,&status,0);if(ret > 0){exitCode = WEXITSTATUS(status); // 获取最近一次进程的退出码}}return 0;
}

进程替换的知识就讲到这了,如有错误还望指出,886!!!


http://www.ppmy.cn/ops/136585.html

相关文章

Docker 数据卷 和 挂载 的区别

在 Docker 中&#xff0c;数据卷&#xff08;Volumes&#xff09;和 挂载&#xff08;Mounts&#xff09;是两种存储和管理容器数据的方式&#xff0c;它们都可以用来持久化容器的数据&#xff0c;但它们之间有一些区别。具体来说&#xff1a; 数据卷 (Volumes) 数据卷是 Dock…

摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现

随着科技的不断进步&#xff0c;摄像机视频分析软件的发展已经为我们的生活带来了许多便捷。其中&#xff0c;LiteAIServer视频智能分析平台的玩手机打电话检测算法技术尤为突出&#xff0c;它利用先进的图像处理和人工智能技术&#xff0c;能够自动识别并监控视频中的玩手机或…

ThingsBoard规则链节点:AWS SNS 节点详解

目录 引言 1. AWS SNS 节点简介 2. 节点配置 2.1 基本配置示例 3. 使用场景 3.1 设备报警 3.2 数据同步 3.3 用户通知 4. 实际项目中的应用 4.1 项目背景 4.2 项目需求 4.3 实现步骤 5. 总结 引言 ThingsBoard 是一个开源的物联网平台&#xff0c;提供了设备管理、…

string类的认识

一、初始string类 在 C 中&#xff0c;std::string是标准库提供的用于处理字符串的类。它提供了比 C 风格字符串&#xff08;以\0结尾的字符数组&#xff09;更方便、更安全的字符串处理方式。 要使用std::string&#xff0c;需要包含<string>头文件。并且&#xff0c;st…

Vulnhub靶场 Jangow: 1.0.1 练习

目录 0x00 准备0x01 主机信息收集0x02 站点信息收集0x03 漏洞查找与利用1. 命令执行2. 反弹shell3. 提权4. 补充4.1 其他思路4.2 问题 0x04 总结 0x00 准备 下载链接&#xff1a;https://download.vulnhub.com/jangow/jangow-01-1.0.1.ova 介绍&#xff1a; Difficulty: easy…

[Unity Demo]从零开始制作空洞骑士Hollow Knight第二十集:制作专门渲染HUD的相机HUD Camera和画布HUD Canvas

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作HUD Camera以及让两个相机同时渲染屏幕二、制作HUD Canvas 1.制作法力条Soul Orb引入库2.制作生命条Health读入数据3.制作吉欧统计数Geo Counter4.制作…

【jvm】从字节码角度看待对象创建流程

目录 1. 分配内存空间2. 初始化内存空间为零值3. 设置对象头4. 调用构造函数初始化对象5. 示例代码6. 字节码指令解析 1. 分配内存空间 1.在Java中&#xff0c;对象存储在堆&#xff08;Heap&#xff09;内存中。2.当创建一个新对象时&#xff0c;JVM首先需要为对象分配一块内…

ONNX 输入batch修改

ONNX 输入batch修改 导出的onnx模型分为静态和动态输入两种&#xff0c;但一般用户会在导出后进行onnxsim操作&#xff0c;导致某些非全卷积的模型修改batch失败&#xff0c;比如transformer类其中reshape的attr属性会固定&#xff0c;修改相当麻烦&#xff0c;需要从源头重新…