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

[Linux]进程替换

🥁作者华丞臧.
📕​​​​专栏:【LINUX】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注)。如果有错误的地方,欢迎在评论区指出。
推荐一款刷题网站 👉 LeetCode刷题网站


文章目录

  • 一、进程替换
    • 1.1 替换原理
    • 1.2 替换函数
  • 二、exec函数
    • 2.1 execl
    • 2.2 execv
    • 2.3 execlp
    • 2.5 execle
    • 2.4 execvp
    • 2.5 execve
  • 补充快捷键
    • 批量化注释
    • 批量化取消注释


一、进程替换

前面我们学习了如何创建子进程,也知道了子进程执行的是父进程的代码片段;那么如果我们想让创建出来的子进程执行全新的程序呢?这时候就需要进程的程序替换。

一般在Linux编程的时候,需要子进程做两类事情:

  1. 让子进程执行父进程的代码片段(服务器代码);
  2. 让子进程执行磁盘中的一个全新的程序,如:shell可以让客户端执行其他人写的代码。

1.1 替换原理

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

程序替换原理总结

  1. 将磁盘中的程序,加载到内存结构;
  2. 重新建立页表映射,谁执行程序替换,就重新建立谁的页表映射效果;让我们的父进程和子进程彻底分离,并让子进程执行一个全新的程序。

注意在程序替换的过程中没有创建新的进程,这个过程指的是程序替换的过程,此时子进程已经创建好了不算新创建的进程。

1.2 替换函数

Linux中有六种exec开头的函数,统称exec函数:
以下六种函数都可以用来进程替换。
在这里插入图片描述

#include <unistd.h>`int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

函数解释:

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回;
  • 如果调用出错则返回-1。
  • 所以exec函数只有出错的返回值而没有成功的返回值。

命名理解:
这些函数原型看起来很容易混,但只要掌握了规律就很好记。

  • l(list):表示参数采用列表;
  • v(vector):参数用数组
  • p(path):有p自动搜索环境变量PATH;
  • e(env):表示自己维护环境变量;
函数名参数格式是否带路径是否使用当前环境变量
execl列表不是
execlp列表
execle列表不是不是,必须自己组装环境变量
execv数组不是
execvp数组
execve数组不是不是,须自己组装环境变量

二、exec函数

接下来我们挑几个exec函数来说明其使用方式。

2.1 execl

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

说明

  • path:程序所在的路径。
  • arg:替换的程序名。
  • ...:可变参数列表,这个我们在使用printf和scanf的时候见过。
  • execl函数替换失败返回-1;

首先使用exec函数之前,我们需要知道如果想执行一个全新的程序,需要做下面两件事:

  1. 找到执行程序路径;
  2. 程序可能携带选项进行执行,也可以不携带;

这里我们可以参照Linux上的命令行是如何使用的,如下:
在这里插入图片描述
Linux中的指令本质上就是程序,所以命令行怎么写我们程序携带选项时传参就怎么写。既然Linux中的指令也是程序,我们首先来试试用指令来替换我们写的程序,代码如下:

#include <stdio.h>
#include <unistd.h>int main()
{printf("进程pid:%d\n", getpid());// arg表示目标程序名,而...参数列表必须以NULL结尾execl("/usr/bin/ls", "ls", NULL); //不带参数//execl("/usr/bin/ls", "ls", "-al", NULL);  //带参数printf("进程替换成功\n");return 0;
}

不带参数的如下图:
在这里插入图片描述
带参数的如下图:
在这里插入图片描述
观察上述两幅图片,我们发现代码中execl下面的那句 printf并没有执行;这是因为一旦替换成功,会将当前进程的代码和数据全部替换了,因此执行完execl函数进程中的代码和数据已经全部被替换了
程序替换函数也有返回值,为int类型;这个返回值不需要判断,一旦进程替换成功就不会有返回值,而替换失败必然会继续向后执行,这个返回值最多让我们知道是什么原因导致替换失败。

使用fork创建子进程,再将子进程进行程序替换,会不会影响父进程呢?

  • 答案是不会,下面我们来实验其过程。
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>int main()
{printf("进程pid:%d\n", getpid());pid_t id = fork();if(id == 0){//childprintf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid());execl("/usr/bin/ls", "ls", "-al", NULL);exit(1);}//parentprintf("这是父进程pid:%d,ppid:%d\n", getpid(), getppid());pid_t ret = waitpid(id, NULL, 0);if(ret > 0) printf("子进程回收成功,ret:%d\n", ret);return 0;
}

在这里插入图片描述
可以看到子进程进行程序替换不会影响父进程,因为进程具有独立性,在数据层面发生写时拷贝:当程序替换的时候,可以理解为代码和数据都发生了写时拷贝,完成了父子进程的分离

2.2 execv

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

函数说明:

  • path:目标程序的路径;
  • argv:数组用来传参;
  • 替换失败返回-1,其中v表示数组(vector)。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>int main()
{printf("进程pid:%d\n", getpid());pid_t id = fork();if(id == 0){//childprintf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid());// 使用argv进行传参,数组中第一个元素argv[0]必须为目标程序名//argv也必须以NULL结尾//char *const myenv[] = {//    (char*)"ls",//    (char*)"-a",//    (char*)"-l",//    NULL//};char *const myenv[] = {(char*)"pwd",NULL};//execv("/usr/bin/ls", myenv);execv("/usr/bin/pwd", myenv);exit(1);}//parentprintf("这是父进程pid:%d,ppid:%d\n", getpid(), getppid());pid_t ret = waitpid(id, NULL, 0);if(ret > 0) printf("子进程回收成功,ret:%d\n", ret);return 0;
}

在这里插入图片描述
在这里插入图片描述

2.3 execlp

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

函数说明:

  • file:目标程序名;
  • arg:参数,传目标程序名;
  • ...:可变参数列表;
  • 函数命名带p表示可以不带路径,只需要说明目标程序名;系统会通过环境变量PATH查找。
  • 替换失败返回-1。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>int main()
{printf("进程pid:%d\n", getpid());pid_t id = fork();if(id == 0){//childprintf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid()); // 使用execlp,可以不带路径:// 其中两个ls含义不同// 第一个为供系统查找,后面一个加上选项表示如何执行execlp("ls", "ls", "-a", NULL);exit(1);}//parentprintf("这是父进程pid:%d,ppid:%d\n", getpid(), getppid());pid_t ret = waitpid(id, NULL, 0);if(ret > 0) printf("子进程回收成功,ret:%d\n", ret);return 0;
}

在这里插入图片描述

2.5 execle

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

函数说明:

  • path:目标程序所在路径;
  • arg:参数,传目标程序名;
  • ...:可变参数列表;
  • envp:用户传给目标程序的环境变量;
  • 失败返回-1。

使用exec函数也可以替换用户自己写的程序:

//replace.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>int main()
{printf("进程pid:%d\n", getpid());pid_t id = fork();if(id == 0){//childprintf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid());char *const env_[] = {(char*)"MYPATH=HELLOWORLD",NULL};execle("./mytest", "mytest", NULL, env_);exit(1);}//parentprintf("这是父进程pid:%d,ppid:%d\n", getpid(), getppid());pid_t ret = waitpid(id, NULL, 0);if(ret > 0) printf("子进程回收成功,ret:%d\n", ret);return 0;
}//mytest.cpp
#include <iostream>
#include <stdlib.h>using namespace std;int main()
{cout << "PATH: " << getenv("PATH") << endl;//cout << "MYPATH: " << getenv("MYPATH") << endl;cout << "hello world1" << endl;cout << "hello world2" << endl;cout << "hello world3" << endl;cout << "hello world4" << endl;cout << "hello world5" << endl;return 0;
}

在这里插入图片描述
如上图,当直接使用execle函数时,进程替换成功了但是运行时进程崩溃了,可以看到此时进程中找不到PATH这个环境变量,那么MYPATH(这是execle函数的传参)呢?

在这里插入图片描述
因此可以得出结论:e的exec函数会添加环境变量给目标进程,执行的是覆盖式的

//这里是使用execl替换代码片段
if(id == 0){//childprintf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid());execl("./mytest", "mytest", NULL);exit(1);}

在这里插入图片描述
使用execl函数PATH环境变量可以正常打印。

如果想要将系统的环境变量保存并且也可以使用用户自己的环境变量,可以使用export指令添加环境变量:

//命令行
export MYPATH="HELLO WORLD"

在这里插入图片描述

2.4 execvp

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

函数说明:

  • file:目标程序名;
  • argv:按照命令行参数格式统一将程序名和选项字符串放入数组中;
  • 命名带p表示会使用环境变量PATH。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>int main()
{printf("进程pid:%d\n", getpid());pid_t id = fork();if(id == 0){//childprintf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid());char *const myenv[] = {(char*)"ls",(char*)"-a",(char*)"-l",NULL};execvp("ls", myenv);exit(1);}//parentprintf("这是父进程pid:%d,ppid:%d\n", getpid(), getppid());pid_t ret = waitpid(id, NULL, 0);if(ret > 0) printf("子进程回收成功,ret:%d\n", ret);return 0;
}

在这里插入图片描述

2.5 execve

与其他的exec函数不同的是:execve函数是系统调用接口。

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

前面学习的那些exec函数基本都是对系统调用接口直接或者间接的封装,适应与不同的适用场景。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>int main()
{printf("进程pid:%d\n", getpid());pid_t id = fork();if(id == 0){//childprintf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid());char *const myenv[] = {(char*)"ls",(char*)"-a",(char*)"-l",NULL};char *const env_[] = {(char*)"MYPATH=HELLOWORLD",NULL};execve("./mytest", myenv, env_);exit(1);}//parentprintf("这是父进程pid:%d,ppid:%d\n", getpid(), getppid());pid_t ret = waitpid(id, NULL, 0);if(ret > 0) printf("子进程回收成功,ret:%d\n", ret);return 0;
}

在这里插入图片描述

补充快捷键

批量化注释

  1. ctrl + v(按一次即可),从需要注释的第一行开始,然后使用HJKL上下左右键选中区域;如果vim被配置过,键盘上的箭头上下左右功能可能改变,因此建议使用HJKL。
    在这里插入图片描述
  2. 切换大写输入I,输入//,再按下Esc就可以完成批量化注释。
    在这里插入图片描述

批量化取消注释

  1. ctrl + v(按一次即可),从需要取消注释的第一行开始,然后使用HJKL上下左右键选中区域;

  2. 输入d就可以完成批量化取消注释。 在这里插入图片描述

相关文章:

[Linux]进程替换

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【LINUX】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文…...

常见的锁策略面试题

你是怎么理解乐观锁和悲观锁的&#xff0c;具体怎么实现呢&#xff1f; 悲观锁认为多个线程访问同一个共享变量冲突的概率较大, 会在每次访问共享变量之前都去真正加锁 乐观锁认为多个线程访问同一个共享变量冲突的概率不大. 并不会真的加锁, 而是直接尝试访问数据. 在访问的…...

设计师一定要知道这几个网站,解决你80%的设计素材。

本期推荐一波设计师必备的设计素材网站&#xff0c;设计党赶紧马住&#xff01;能解决你日常设计中80%的素材。 1、菜鸟图库 菜鸟图库-免费设计素材下载 这是一个为新手设计师提供免费素材的设计网站&#xff0c;站内有超多平面模板、海报、UI设计、电商设计等相关素材&#x…...

QT基础入门

学习视频&#xff1a;QT开发概述_哔哩哔哩_bilibili 1.QT开发概述 1.什么是QT QT是一个1991年由Qt Company开发的跨平台C图形用户界面应用程序开发框架。它既可以开发GUI程序&#xff0c;也可用于开发非GUI程序&#xff0c;比如控制台工具和服务器。Qt是面向对象的框架&#…...

高数不定积分72题解答

题目来源&#xff1a;这72道积分题目会积了&#xff0c;绝对是高高手 作者&#xff1a; 湖心亭看雪 第一题 原式∫15x3dx15∫15x3d(5x3)15ln(5x3)C\begin{aligned} \text{原式}&\int \frac{1}{5x3}dx \\ &\frac{1}{5} \int\frac{1}{5x3}d(5x3) \\ &\frac{1}{5} ln…...

基于北方苍鹰算法优化LSTM(NGO-LSTM)研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Linux内核启动(理论,0.11版本)分段与分页

为什么要虚拟内存 我们知道&#xff0c;在之前上微机原理时&#xff0c;我们的程序是可以直接访问内存的&#xff0c;而且访问的是直接的物理内存&#xff0c;在实模式下&#xff0c;寄存器是16位的&#xff0c;数组总线&#xff08;data bus&#xff09;是16位的&#xff0c;…...

数据与C(字符串)

目录 一.概念引入 二.字符串&#xff08;数组存储&#xff0c;必须以\0结尾&#xff09; 三.错误示范 四.strlen&#xff08;&#xff09;和sizeof()相对于字符串的不同 一.概念引入 “a”,a哪个是字符哪个又是字符串&#xff0c;嘿嘿不用猜了 我们在上一章中说过&#x…...

Python+Go实践(电商架构三)

文章目录服务发现集成consul负载均衡负载均衡算法实现配置中心nacos服务发现 我们之前的架构是通过ipport来调用的python的API&#xff0c;这样做的弊端是 如果新加一个服务&#xff0c;就要到某个服务改web&#xff08;go&#xff09;层的调用代码&#xff0c;配置IP/Port并发…...

基于 MySQL 排它锁实现分布式可重入锁解决方案

一、MySQL 排它锁和共享锁 在进行实验前&#xff0c;先来了解下MySQL 的排它锁和共享锁&#xff0c;在 MySQL 中的锁分为表锁和行锁&#xff0c;在行锁中锁又分成了排它锁和共享锁两种类型。 1. 排它锁 排他锁又称为写锁&#xff0c;简称X锁&#xff0c;是一种悲观锁&#x…...

【大数据】Hadoop-HA-Federation-3.3.1集群高可用联邦安装部署文档(建议收藏哦)

背景概述 单 NameNode 的架构使得 HDFS 在集群扩展性和性能上都有潜在的问题&#xff0c;当集群大到一定程度后&#xff0c;NameNode 进程使用的内存可能会达到上百 G&#xff0c;NameNode 成为了性能的瓶颈。因而提出了 namenode 水平扩展方案-- Federation。 Federation 中…...

【设计模式之美 设计原则与思想:面向对象】14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?

在上一节课中&#xff0c;针对接口鉴权功能的开发&#xff0c;我们讲了如何进行面向对象分析&#xff08;OOA&#xff09;&#xff0c;也就是需求分析。实际上&#xff0c;需求定义清楚之后&#xff0c;这个问题就已经解决了一大半&#xff0c;这也是为什么我花了那么多篇幅来讲…...

工作技术小结

2023/1/31 关于后端接口编写小结 1&#xff0c;了解小程序原型图流程和细节性的东西 2&#xff0c;数据库关联结构仔细分析&#xff0c;找到最容易查询的关键字段&#xff0c;标语表之间靠什么关联 2023/2/10 在web抓包过程中&#xff0c;如果要实现批量抓取&#xff0c;必须解…...

无重复字符的最长子串-力扣3-java

一、题目描述给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。示例 1:输入: s "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。示例 2:输入: s "bbbbb"输出: 1解释: 因为…...

java ssm高校教材管理平台 idea maven

设计并且实现一个基于JSP技术的高校教材管理平台的设计与实现。采用MYSQL为数据库开发平台&#xff0c;SSM框架&#xff0c;Tomcat网络信息服务作为应用服务器。高校教材管理平台的设计与实现的功能已基本实现&#xff0c;主要学生、教材管理、学习教材、教材入库、教材领取、缴…...

【Python学习笔记】25.Python3 输入和输出(1)

前言 在前面几个章节中&#xff0c;我们其实已经接触了 Python 的输入输出的功能。本章节我们将具体介绍 Python 的输入输出。 输出格式美化 Python两种输出值的方式: 表达式语句和 print() 函数。 第三种方式是使用文件对象的 write() 方法&#xff0c;标准输出文件可以用…...

C++复习笔记8

泛型编程&#xff1a;编写的是与类型无关的通用代码&#xff0c;是代码复用的一种手段&#xff0c;模板是泛型编程的基础。 1.函数模板&#xff1a;类型参数化&#xff0c;增加代码复用性。例如对于swap函数&#xff0c;不同类型之间进行交换都需要进行重载&#xff0c;但是函数…...

RabbitMQ入门

目录1. 搭建示例工程1.1. 创建工程1.2. 添加依赖2. 编写生产者3. 编写消费者4. 小结需求 官网&#xff1a; https://www.rabbitmq.com/ 需求&#xff1a;使用简单模式完成消息传递 步骤&#xff1a; ① 创建工程&#xff08;生成者、消费者&#xff09; ② 分别添加依赖 ③ 编…...

【计算机网络】Linux环境中的TCP网络编程

文章目录前言一、TCP Socket API1. socket2. bind3. listen4. accept5. connect二、封装TCPSocket三、服务端的实现1. 封装TCP通用服务器2. 封装任务对象3. 实现转换功能的服务器四、客户端的实现1. 封装TCP通用客户端2. 实现转换功能的客户端五、结果演示六、多进程版服务器七…...

idekCTF 2022 比赛复现

Readme 首先 []byte 是 go 语言里面的一个索引&#xff0c;比如&#xff1a; package mainimport "fmt"func main() {var str string "hello"var randomData []byte []byte(str)fmt.Println(randomData[0:]) //[104 101 108 108 111] }上面这串代码会从…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...