当前位置: 首页 > news >正文

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

文章目录

  • 进程替换
    • 进程替换是什么?
    • 替换的方法
    • 进程替换
  • 简易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};//就是把可变参数列表以数组的形式传给execvprintf("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、进程替换函数、进程等待函数、进程退出函数、退出码/终止信号,加深了这些接口的理解

相关文章:

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

文章目录进程替换进程替换是什么&#xff1f;替换的方法进程替换简易shell模拟进程替换 进程替换是什么&#xff1f; 如下图所示&#xff1a; ​ 进程替换就是&#xff0c;把进程B的代码和数据&#xff0c;替换正在执行的进程A的代码和数据在内存中的位置&#xff08;若代码…...

【C++从入门到放弃】类和对象(中)———类的六大默认成员函数

&#x1f9d1;‍&#x1f4bb;作者&#xff1a; 情话0.0 &#x1f4dd;专栏&#xff1a;《C从入门到放弃》 &#x1f466;个人简介&#xff1a;一名双非编程菜鸟&#xff0c;在这里分享自己的编程学习笔记&#xff0c;欢迎大家的指正与点赞&#xff0c;谢谢&#xff01; 类和对…...

白盒测试重点复习内容

白盒测试白盒测试之逻辑覆盖法逻辑覆盖用例设计方法1.语句覆盖2.判定覆盖(分支覆盖)3.条件覆盖4.判定条件覆盖5.条件组合覆盖6.路径覆盖白盒测试之基本路径测试法基本路径测试方法的步骤1.根据程序流程图画控制流图2.计算圈复杂度3.导出测试用例4.准备测试用例5.例题白盒测试总…...

【13】linux命令每日分享——groupadd建立组

大家好&#xff0c;这里是sdust-vrlab&#xff0c;Linux是一种免费使用和自由传播的类UNIX操作系统&#xff0c;Linux的基本思想有两点&#xff1a;一切都是文件&#xff1b;每个文件都有确定的用途&#xff1b;linux涉及到IT行业的方方面面&#xff0c;在我们日常的学习中&…...

《第一行代码》 第十章:服务

一&#xff0c;在子线程中更新UI 1&#xff0c;新建项目&#xff0c;修改布局代码 <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"&g…...

简单介绍编程进制

十进制 十进制的位权为 10&#xff0c;比如十进制的 123&#xff0c;123 1 * 10 ^ 2 2 * 10 ^ 1 3 * 10 ^ 0。 二进制 二进制的位权为 2&#xff0c;比如十进制的 4&#xff0c;二进制为 100&#xff0c;4 1 * 2 ^ 2 0 * 2 ^ 1 0 *2 ^ 0。 Java7 之前&#xff0c;不支…...

windows忘记开机密码怎么办

windows忘记开机密码怎么办 清除windows登录密码 清除windows登录密码简单方法 开机到欢迎界面时&#xff0c;按CtrlAltDelete两次&#xff0c;跳出帐号窗口&#xff0c;输入用户名&#xff1a;administrator&#xff0c;回车&#xff0c; 或者启动时按F8 选“带命令行的安全…...

SpringCloud:Eureka

目录 一、eureka的作用 二、搭建Eureka服务端 三、添加客户端 四、服务发现 提供者与消费者 服务提供者&#xff1a;一次业务中&#xff0c;被其它微服务调用的服务。&#xff08;提供接口给其它微服务) 服务消费者&#xff1a;一次业务中&#xff0c;调用其它微服务的服…...

如何获取或设置CANoe以太网网卡信息(SET篇)

CAPL提供了一系列函数用来操作CANoe网卡。但是,但是,首先需要明确一点,不管是获取网卡信息,还是设置网卡信息,只能访问CAPL程序所在的节点下的网卡,而不是节点所在的以太网通道下的所有网卡 关于第一张图中,Class节点下,有三个网卡:Ethernet1、VLAN 1.100、VLAN 1.200…...

【软件测试面试题】项目经验?资深测试 (分析+回答) 我不信你还拿不到offer......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 在面试过程中&#…...

tensorflow lite简介-移动设备端机器学习

TensorFlow Lite 是一组工具&#xff0c;可帮助开发者在移动设备、嵌入式设备和 loT 设备上运行模型&#xff0c;以便实现设备端机器学习。 支持多平台 支持多种平台&#xff0c;涵盖 Android 和 iOS 设备、嵌入式 Linux 和微控制器。 原理/流程 工作原理或者使用流程就是上面…...

Node.js常用知识

1、什么是 Node.js 【】Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。浏览器是 js 的前端运行环境&#xff0c;node.js 是 js 的后端运行环境。他们都有 V8 引擎&#xff0c;有各自的内置 API 2、fs 文件系统模块 【】fs 模块是 Node.js 官方提供的、用来操作文件…...

踩坑:maven打包失败的解决方式总结

Maven打包失败原因总结如下&#xff1a; 失败原因1&#xff1a;无法使用spring-boot-maven-plugin插件 使用spring-boot-maven-plugin插件可以创建一个可执行的JAR应用程序&#xff0c;前提是应用程序的parent为spring-boot-starter-parent。 需要添加parent的包spring-boot…...

【C++】位图

文章目录位图概念位图操作位图代码位图应用位图概念 boss直接登场&#xff1a; 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中❓ 40亿个整数&#xff0c;大概就是16GB。40亿个字节大概就是4GB。 1Byt…...

蓝桥杯-考勤刷卡

蓝桥杯-考勤刷卡1、问题描述2、解题思路3、代码实现1、问题描述 小蓝负责一个公司的考勤系统, 他每天都需要根据员工刷卡的情况来确定 每个员工是否到岗。 当员工刷卡时, 会在后台留下一条记录, 包括刷卡的时间和员工编号, 只 要在一天中员工刷过一次卡, 就认为他到岗了。 现在…...

如何利用站内推广和站外推广提高转化率?

在如今的网络时代&#xff0c;拥有一个好的网站是非常重要的。但是&#xff0c;光有一个好的网站是不够的&#xff0c;为了达到我们的目标&#xff0c;需要不断地提高网站的转化率。而在实现这个目标的过程中&#xff0c;站内推广和站外推广是两个非常关键的因素。 站内推广是…...

Java多线程(三)——线程池及定时器

线程池就是一个可以复用线程的技术。前面三种多线程方法就是在用户发起一个线程请求就创建一个新线程来处理&#xff0c;下次新任务来了又要创建新线程&#xff0c;而创建新线程的开销是很大的&#xff0c;这样会严重影响系统的性能。线程池就相当于预先创建好几个线程&#xf…...

Linux命令行安装Oracle19c教程和踩坑经验

安装 下载 从 Oracle官方下载地址 需要的版本&#xff0c;本次安装是在Linux上使用yum安装&#xff0c;因此下载的是RPM。另外&#xff0c;需要说明的是&#xff0c;Oracle加了锁的下载需要登录用户才能安装&#xff0c;而用户是可以免费注册的&#xff0c;这里不做过多说明。 …...

Linux常用命令等

目录 1.Linux常用命令 (1)系统命令 (2)文件操作命令 2.vim编辑器 3.linux系统中,软件安装 (1) rpm 安装,RedHat Package Manager (2)yum 安装 (3)源代码编译安装 1.Linux常用命令 Linux命令是非常多的,对于像嵌入式开发工程师,运维工程师需要掌握的命令是非常多的.对于…...

CEC2014:鱼鹰优化算法(Osprey optimization algorithm,OOA)求解CEC2014(提供MATLAB代码

一、鱼鹰优化算法简介 鱼鹰优化算法&#xff08;Osprey optimization algorithm&#xff0c;OOA&#xff09;由Mohammad Dehghani 和 Pavel Trojovsk于2023年提出&#xff0c;其模拟鱼鹰的捕食行为。 鱼鹰是鹰形目、鹗科、鹗属的仅有的一种中型猛禽。雌雄相似。体长51-64厘米…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!

【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...