[Linux]进程替换

news2024/10/2 14:27:45

🥁作者华丞臧.
📕​​​​专栏:【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)
    {
        //child
        printf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid());
        execl("/usr/bin/ls", "ls", "-al", NULL);
        exit(1);
    }

    //parent
    printf("这是父进程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)
    {
        //child
        printf("这是子进程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);
    }

    //parent
    printf("这是父进程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)
	{
        //child
        printf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid()); 
        // 使用execlp,可以不带路径:
        // 其中两个ls含义不同
        // 第一个为供系统查找,后面一个加上选项表示如何执行
        execlp("ls", "ls", "-a", NULL);
        exit(1);
	}
	
	//parent
    printf("这是父进程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)
    {
        //child
        printf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid());
        char *const env_[] = {
            (char*)"MYPATH=HELLOWORLD",
            NULL
        };
		
        execle("./mytest", "mytest", NULL, env_);
        exit(1);
    }

    //parent
    printf("这是父进程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)
    {
        //child
        printf("这是子进程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)
    {
        //child
        printf("这是子进程pid:%d,ppid:%d\n", getpid(), getppid());
        char *const myenv[] = {
            (char*)"ls",
            (char*)"-a",
            (char*)"-l",
            NULL
        };
 
        execvp("ls", myenv);
        exit(1);
    }

    //parent
    printf("这是父进程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)
    {
        //child
        printf("这是子进程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);
    }

    //parent
    printf("这是父进程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就可以完成批量化取消注释。 在这里插入图片描述

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

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

相关文章

HashMap设计思想学习

HashMap设计思想学习引言树化与退化红黑树的优势索引计算put流程扩容&#xff08;加载&#xff09;因子为何默认是 0.75fhashMap并发丢失数据问题jdk 1.7并发死链问题key 的设计引言 hashmap在jdk 1.7之前是数组链表结构&#xff0c;而jdk1.8之后变为是数组(链表|红黑树) 树化…

【第38天】不同路径数问题 | 网格 dp 入门

本文已收录于专栏&#x1f338;《Java入门一百例》&#x1f338;学习指引序、专栏前言一、网格模型二、【例题1】1、题目描述2、解题思路3、模板代码4、代码解析5.原题链接三、【例题2】1、题目描述2、解题思路3、模板代码4、代码解析5.原题链接三、推荐专栏四、课后习题序、专…

大型物流运输管理系统源码 TMS源码

大型物流运输管理系统源码 TMS是一套适用于物流公司的物流运输管理系统&#xff0c;涵盖物流公司内部从订单->提货->运单->配车->点到->预约->签收->回单->代收货款的全链条管理系统。 菜单功能 一、运营管理 1、订单管理&#xff1a;用于客户意向订…

ChatGPT 到底强大在哪里?(文末有彩蛋)

ChatGPT 是由 OpenAI 开发的一个人工智能聊天机器人程序&#xff0c;于2022年11月推出。该程序使用基于 GPT-3.5 架构的大型语言模型并通过强化学习进行训练。ChatGPT 以文字方式交互&#xff0c;而除了可以通过人类自然对话方式进行交互&#xff0c;还可以用于相对复杂的语言工…

一定要收藏的面试思维导图,粉丝分享面试经验

一位粉丝分享面试经验&#xff1a;1.常见面试题有哪些&#xff1f;主要从以下一些知识点做了准备&#xff1a; 常用的分析方法、Excel、SQL、 A/B测试、产品分析。然后每份面试针对职位要求&#xff0c;还有前期和HR聊天一点点了解这个职位之后&#xff0c;定向准备。 Excel、S…

OpenMMLab 实战营打卡 - 第 7 课

OpenMMLab MMSegmentation内容概要MMSegmentation统一超参MMSegmentation 的项目结构分割模型的模块化设计分割模型的配置文件主干网络的配置ResNet v1c主解码头的配置辅助解码头的配置数据集配置数据处理流水线常用训练策略参考资料内容概要 • MMSegmentation 项目概述 • M…

TSDF算法应用与源码详解

数据与代码链接见文末 1.TSDF整体概述 (1)需要准备的输入 1.原始图片;2.对应的深度信息;3.每张图的相机位姿;4.相机内参 原始输入图像数据(就是一个场景多个视角拍摄的结果) 输入图像的深度信息,位姿信息等(一般由相机得到,也可以通过算法得到) (2)整体概述…

【DFS并查集】岛屿数量

经典的dfs/bfs问题&#xff0c;给一个起点开始搜索&#xff0c;满足条件则继续调用dfs/bfs 从没有访问过的某个陆地出发&#xff0c;将所有能到达的陆地的状态都记录为已访问。下次出发不从已访问的陆地出发&#xff0c;每次出发前都把岛屿数 1即可 class Solution { public…

STM32开发(9)----CubeMX配置外部中断

CubeMX配置外部中断前言一、什么是中断1.STM32中断架构体系2.外部中断/事件控制器&#xff08;EXTI&#xff09;3.嵌套向量中断控制器&#xff08;NIVC&#xff09;二、实验过程1.CubeMX配置2.代码实现3.硬件连接4.实验结果总结前言 本章介绍使用STM32CubeMX对引脚的外部中断进…

MySQL 3:MySQL数据库基本操作 DQL

数据库管理系统的一个重要功能是数据查询。数据查询不应简单地返回数据库中存储的数据&#xff0c;还应根据需要对数据进行过滤&#xff0c;确定数据的显示格式。MySQL 提供了强大而灵活的语句来实现这些操作。MySQL数据库使用select语句查询数据。 select [all|distinct]<…

09- 机器学习经典流程 (中国人寿保费项目) (机器学习)

删除特征: data data.drop([region, sex], axis1)特征数据调整: data.apply( ) # 体重指数&#xff0c;离散化转换&#xff0c;体重两种情况&#xff1a;标准、肥胖 def convert(df,bmi):df[bmi] fat if df[bmi] > bmi else standardreturn df data data.apply(convert, …

EXCEL-职业版本(1)

EXCEL职业版本(1) 工作表 插入 注&#xff1a;默认会在鼠标选中的sheet后面新增&#xff0c;例如图中&#xff0c;当前选中的是sheet2&#xff0c;点击新增后会在sheet2后面自动新增一个sheet 删除 移动或者复制 类似于copy一整个sheet的所有内容 step1 右击【sheet名称】选…

Cordova

一、简介 Cordova 是用 Web 技术&#xff08; HTML&#xff0c;CSS 和 JS &#xff09;构建移动应用的平台。我们可以认为Cordova 是一个容器&#xff0c;用于将的 Web 应用移植到移动端&#xff0c;同时支持移动端的功能&#xff08;例如&#xff1a;定位、蓝牙、摄像头等&am…

Linux内核并发与竞争-原子操作

一.原子操作的概念首先看一下原子操作&#xff0c;原子操作就是指不能再进一步分割的操作&#xff0c;一般原子操作用于变量或者位操作。假如现在要对无符号整形变量 a 赋值&#xff0c;值为 3&#xff0c;对于 C 语言来讲很简单&#xff0c;直接就是&#xff1a; a3但是 C 语言…

机器学习基本原理总结

本文大部分内容参考《深度学习》书籍&#xff0c;从中抽取重要的知识点&#xff0c;并对部分概念和原理加以自己的总结&#xff0c;适合当作原书的补充资料阅读&#xff0c;也可当作快速阅览机器学习原理基础知识的参考资料。 前言 深度学习是机器学习的一个特定分支。我们要想…

Elasticsearch bucket_script、bucket_selector、bucket_sort 区别和应用场景?

1、实战问题POST test-002/_bulk {"index":{"_id":1}} {"name": "张三","city": "beijing"} {"index":{"_id":2}} {"name": "李四","city": "beijing&qu…

简信CRM:医疗健康行业数字化解决方案

随着社会群体健康意识提升、用户消费习惯变化、新冠疫情冲击等因素&#xff0c;人们对于个人和公共安全健康问题就越发重视&#xff0c;而且已经逐渐从对“病”的被动治疗转变为对“健康”的主动管理&#xff0c;医疗健康行业呈现出一片火热的趋势。但医疗健康行业的情况比较复…

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] }上面这串代码会从…

Java程序运行机制

Java语言既具有编译型语言的特征&#xff0c;又具有解释型语言的特征&#xff0c;Java程序要经过先编译后解释两个阶段。高级语言的运行机制&#x1f4cd;编译型语言使用专门的编译器&#xff0c;针对特定的平台&#xff08;移植性差&#xff09;&#xff0c;将高级语言的源代码…

情人节有哪些数码好物值得送礼?情人节实用性强的数码好物推荐

转瞬间&#xff0c;情人节快到了&#xff0c;大家还在为送什么礼物而烦恼&#xff1f;在这个以科技为主的时代&#xff0c;人们正在享受着科技带来的便利&#xff0c;其中&#xff0c;数码产品也成为了日常生活中必不可少的存在。接下来&#xff0c;我来给大家推荐几款比较实用…